Spring -- aop(面向切面编程),前置&后置&环绕&抛异常通知,引入通知,自动代理
1.概要
aop:面向方面编程.不改变源代码,还为类增加新的功能.(代理)
切面:实现的交叉功能.
通知:切面的实际实现(通知要做什么,怎么做).
连接点:应用程序执行过程期间,可以插入切面的地点.
切入点:真正的将通知应用到目标程序中的地点,一定是连接点.切入点是连接点的子集.
切入点决定了一个特定的类的特定方法是否满足一定的规则
引入:为类增加新的属性和方法. (引入通知)
目标对象:被通知的对象.
代理:把通知应用到目标对象以后,产生新的对象,该对象就称为代理对象.
织入:创建代理对象过程.
编译期织入:.java --> .class,需要特殊的编译器.
类装载期织入:将java字节码载入到jvm时,将通知织入.需要特殊的classloader.
运行期(runtime): cglib: aop alliance:aop联盟. spring代理方案:
1.接口代理:jdk动态代理,创建更加松耦合的系统.
2.对类代理:cglib代理,final方法无法被代理. spring aop编程:
1.aop alliance.jar(已经集成在spring.jar中) + cglib.
${spring解压目录}/lib/cglib/*.jar
2.加入aspectj类库
${{spring解压目录}/lib/aspectj/*.jar(aspectjrt.jar + aspectjweaver.jar)
3.创建接口和实现类
public interface WelcomeService {
public void sayName();
}
/**
* 目标类
*/
public class WelcomeServiceImpl implements WelcomeService {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public void sayName() {
System.out.println(name);
}
}
4.创建前置通知.
/**
* 前置通知(方法前通知)
*/
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("hello world");
}
}
5.配置文件.
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- 前置通知(方法前通知) -->
<bean id="myMethodBeforeAdvice" class="cn.itcast.spring.aop.advice.MyMethodBeforeAdvice" /> <!-- 目标对象 -->
<bean id="welcomeServiceTarget" class="cn.itcast.spring.aop.service.WelcomeServiceImpl">
<property name="name" value="tom" />
</bean> <!-- 代理对象 -->
<bean id="welcomeService" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理接口集 -->
<property name="proxyInterfaces">
<list>
<value>cn.itcast.spring.aop.service.WelcomeService</value>
</list>
</property>
<!-- 拦截器名集 -->
<property name="interceptorNames">
<list>
<value>myMethodBeforeAdvice</value>
</list>
</property>
<!-- 指定目标对象 -->
<property name="target" ref="welcomeServiceTarget" />
</bean>
</beans>
6.App
ApplicationContext ac = new ClassPathXmlApplicationContext(
"cn/itcast/spring/aop/aop.xml");
WelcomeService ws = (WelcomeService) ac.getBean("welcomeServiceTarget");
ws.sayName(); public interface Pointcut{
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
} Pointcut:切入点
Advice:通知
Advisor:切入点通知,组合体,既包含通知又包含切入点.对原来的通知的包装,增加定义切入点功能 PointcutAdvisor{
Pointcut getPointcut();
Advice getAdvice();
} 引入通知:
1.定义引入通知. Dao:data access object.数据(数据库的表数据)访问对象. dto:data transfer object,数据传输对象. struts1(actionform jsp --> action) 集成dao.
1.引入数据源类库
${spring解压目录}/lib/c3p0/*.jar
c3p0-0.9.1.2.jar
2.配置spring配置文件,链接数据源
2. AOP介绍
1.切面(aspect):要实现的交叉功能,是系统模块化的一个切面或领域。如日志记录。
2.连接点:应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段。
3.通知:切面的实际实现,他通知系统新的行为。如在日志通知包含了实现日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。
4.切入点:定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点。
5.引入:为类添加新方法和属性。
6.目标对象:被通知的对象。既可以是你编写的类也可以是第三方类。
7.代理:将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变。
8.织入:将切面应用到目标对象从而创建一个新代理对象的过程。织入发生在目标对象生命周期的多个点上:
编译期:切面在目标对象编译时织入.这需要一个特殊的编译器.
类装载期:切面在目标对象被载入JVM时织入.这需要一个特殊的类载入器.
运行期:切面在应用系统运行时织入.
spring有两种代理方式:
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。该类让spring动态产生 一个新类,它实现了所需的接口,织入了通知,并且代理对目标对象的所有请求。
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。使用该方式时需要注意:
1.对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是备用方案。
2.标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。
spring实现了aop联盟接口。
spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用的得到的结果。
简单的ClassFilter接口实现-ClassFilter.TRUTE。它是规范的适合任何类的ClassFilter实例,适合用于只根据方法决定时候符合要求的切入。ClassFilter通过类过滤切面,MethodMatcher通过方法过滤切面。
public interface MethodMatcher{
boolean matches(Method m,Class targetClass);1.
boolean isRuntime();2.
boolean matchers(Method m,Class target,Object[] args);3.
}
1. 根据目标类和方法决定方法是否被通知。因为可以静态的判断,所以可以在AOP代理被创建时候调用一次这个方法。该方法的结果最终决定了通知是否被织入。
如果1.返回true,2.被调用来决定MethodMatcher的类型。有两种类型:静态和动态。静态切入点的意思是通知总是被执行。如果一个切入点是静态的,该方法返回false.动态切入点根据运行时方法的参数值决定通知是否需要执行。如果切入点是动态的,该方法返回true。和1.方法类似,该方法也是在代理创建时运行一次。
如果切入点是静态的,3.永远不会执行,对于动态切入点,需要根据运行时的参数决定方法是否被通知,所以会增加系统的负担,尽量使用静态切入点。
正则表达式切入点 RegexpMethodPointcut
使用spring的静态切入点
<bean id="xxxTarget" class="xxxServiceImpl"/>
<bean id="xxxAdvice" class=""/>
<bean id="xxxAdvisor" class="…RegExpPointcutAdvisor">
<property name="pattern">
<value>.*get.+bar.+<value>
</property>
<property name="advice">
<ref bean="xxxAdvice" />
</property>
</bean>
<bean id="xxxService" class="…ProxyFactoryBean">
<property name="proxyInterfaces">
<value>……xxxService</value>
</property>
<property name="interceptorNames">
<list><value>xxxAdvisor</value></list>
</property>
<property name="target"><ref bean="xxxTarget" /></property>
</bean>
该规则表示任何类的以get开头,后面至少有一个字符,然后跟着by,后面至少有一个字符的方法
使用动态切入点
<bean id="xxxTarget" class="xxxServiceImpl"/>
<bean id="xxxAdvice" class=""/>
<bean id="xxxPointcut" class="…ControlFlowPointcut">
<contructor-arg">
<value>javax.servlet.http.HttpServlet</value>
</contructor-arg>
</bean>
<bean id="xxxAdvisor" class="…DefaultPointcutAdvisor">
<property name="advice">
<ref bean="xxxAdvice" />
</property>
<property name="pointcut">
<ref bean="xxxPointcut">
</property>
</bean>
<bean id="xxxService" class="……ProxyFactoryBean">
……
</bean>
有许多类需要通知时,显式的创建每个代理就会显得很笨拙。spring有一个自动代理机制,它可以让容器为我们产生代理。
BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator
示例代码:
service层代码:接口及接口实现
WelcomeService.java 接口1
public interface WelcomeService {
public void sayName();
}
WelcomeService2.java 接口2
public interface WelcomeService2 {
public void sayName2();
}
ModifyDate.java 接口3
public interface ModifyDate {
public void setModifyDate(Date date);
public Date getModifyDate();
}
WelcomeServiceImpl.java 接口实现
public class WelcomeServiceImpl implements WelcomeService,WelcomeService2 {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public void sayName() {
System.out.println(name);
// String str = null ; //制造异常
// str.toCharArray();
} public void sayName2() {
System.out.println("kkk");
}
}
advice, 通知
MyMethodBeforeAdvice.java 前置通知
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("before");
}
}
MyAfterReturningAdvice.java 后置通知
public class MyAfterReturningAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("after");
}
}
MyMethodInterceptor.java 环绕通知
public class MyMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("begin");
//调用目标对象的方法
Object res = arg0.proceed();
System.out.println("end");
return res;
}
}
MyThrowsAdvice.java 异常通知
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method m,Object[] os,Object
target,Throwable throwable){
System.out.println(m);
System.out.println(target);
System.out.println("出事了!" + throwable.toString());
}
}
MyDII.java 引入通知
/**
* 引入通知(代理引入拦截器)
*/
public class MyDII extends DelegatingIntroductionInterceptor implements
ModifyDate { private static final long serialVersionUID = 1926256070698432626L;
private Date modifyDate ;
public Date getModifyDate() {
return modifyDate;
} public void setModifyDate(Date date) {
this.modifyDate = date ;
}
}
App.java 测试程式
public class App {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext(
"cn/itcast/spring/aop/aop.xml");
WelcomeService ws = (WelcomeService) ac.getBean("welcomeService");
ws.sayName();
WelcomeService2 ws2 = (WelcomeService2) ws;
ws2.sayName2(); ((ModifyDate)ws).setModifyDate(new Date()); //相当于实现多重继承
System.out.println(((ModifyDate)ws).getModifyDate());
}
}
aop.xml 配置文件
<?xml version="1.0"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!-- 前置通知(方法前通知) -->
<bean id="myMethodBeforeAdvice" class="cn.itcast.spring.aop.advice.MyMethodBeforeAdvice" />
<!-- 后置通知(方法返回后通知) -->
<bean id="myAfterReturningAdvice" class="cn.itcast.spring.aop.advice.MyAfterReturningAdvice" />
<!-- 环绕通知(方法拦截器) -->
<bean id="myMethodInterceptor" class="cn.itcast.spring.aop.advice.MyMethodInterceptor" />
<!-- 环抛出异常知-->
<bean id="myThrowsAdvice" class="cn.itcast.spring.aop.advice.MyThrowsAdvice" />
<!-- 引入通知(代理引入拦截器) -->
<bean id="myDII" class="cn.itcast.spring.aop.advice.MyDII" /> <!-- 名称匹配方法切入点通知(对原来的通知的包装,增加定义切入点功能) -->
<bean id="beforeAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myMethodBeforeAdvice" />
<property name="mappedNames">
<list>
<value>sayName</value>
</list>
</property>
</bean>
<!-- 后置通知包装 -->
<bean id="afterAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="myAfterReturningAdvice" />
<property name="mappedNames">
<list>
<value>sayName2</value>
</list>
</property>
</bean>
<!-- 默认的引入切入点通知(类似于名称匹配方法切入点通知) -->
<bean id="defaultIntroductionAdvisor"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg ref="myDII" />
</bean> <!-- 目标对象 -->
<bean id="welcomeServiceTarget" class="cn.itcast.spring.aop.service.WelcomeServiceImpl">
<property name="name" value="tom" />
</bean> <!-- 代理对象, 不推荐使用proxyTargetClass
<bean id="welcomeService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>cn.itcast.spring.aop.service.WelcomeService</value>
<value>cn.itcast.spring.aop.service.WelcomeService2</value>
<value>cn.itcast.spring.aop.service.ModifyDate</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
<value>afterAdvisor</value>
<value>myMethodInterceptor</value>
<value>myThrowsAdvice</value>
<value>defaultIntroductionAdvisor</value>
</list>
</property>
<property name="target" ref="welcomeServiceTarget" />
<property name="proxyTargetClass" value="true" />
</bean>
-->
<!-- bean名自动代理创建器,按照bean的名称自动创建代理对象
<bean id="beanNameAutoProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
<value>afterAdvisor</value>
<value>myMethodInterceptor</value>
<value>myThrowsAdvice</value>
<value>defaultIntroductionAdvisor</value>
</list>
</property>
<property name="beanNames">
<list>
<value>*ServiceTarget</value>
</list>
</property>
</bean>
--> <!-- 默认的切入点通知自动代理创建器(将所有的advisor应用到所有的业务bean上去,能否加通
知,要看是否满足切入点的要求) -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
</beans>
Spring -- aop(面向切面编程),前置&后置&环绕&抛异常通知,引入通知,自动代理的更多相关文章
- 详细解读 Spring AOP 面向切面编程(二)
本文是<详细解读 Spring AOP 面向切面编程(一)>的续集. 在上篇中,我们从写死代码,到使用代理:从编程式 Spring AOP 到声明式 Spring AOP.一切都朝着简单实 ...
- 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring ...
- spring AOP面向切面编程学习笔记
一.面向切面编程简介: 在调用某些类的方法时,要在方法执行前或后进行预处理或后处理:预处理或后处理的操作被封装在另一个类中.如图中,UserService类在执行addUser()或updateUse ...
- 【spring-boot】spring aop 面向切面编程初接触--切点表达式
众所周知,spring最核心的两个功能是aop和ioc,即面向切面,控制反转.这里我们探讨一下如何使用spring aop. 1.何为aop aop全称Aspect Oriented Programm ...
- 【spring-boot】spring aop 面向切面编程初接触
众所周知,spring最核心的两个功能是aop和ioc,即面向切面,控制反转.这里我们探讨一下如何使用spring aop. 1.何为aop aop全称Aspect Oriented Programm ...
- 【Spring系列】Spring AOP面向切面编程
前言 接上一篇文章,在上午中使用了切面做防重复控制,本文着重介绍切面AOP. 在开发中,有一些功能行为是通用的,比如.日志管理.安全和事务,它们有一个共同点就是分布于应用中的多处,这种功能被称为横切关 ...
- 从源码入手,一文带你读懂Spring AOP面向切面编程
之前<零基础带你看Spring源码--IOC控制反转>详细讲了Spring容器的初始化和加载的原理,后面<你真的完全了解Java动态代理吗?看这篇就够了>介绍了下JDK的动态代 ...
- Spring AOP面向切面编程详解
前言 AOP即面向切面编程,是一种编程思想,OOP的延续.在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等.在阅读本文前希望您已经对Spring有一定的了解 注:在能对代码进行添 ...
- Spring AOP 面向切面编程入门
什么是AOP AOP(Aspect Oriented Programming),即面向切面编程.众所周知,OOP(面向对象编程)通过的是继承.封装和多态等概念来建立一种对象层次结构,用于模拟公共行为的 ...
随机推荐
- Powershell About LocalGroupMembership
一: 结合active directory获取本地群组成员信息(包含本地用户和域用户,及域用户的情况 $DBServer = "xxxx" $DBDatabase = " ...
- Centos7 下安装mysql数据库
centos7系统,安装mysql发现已经默认的是mariadb. 只能安装mariadb,mariadb是mysql一个分支,对mysql完全支持 1 安装 yum -y install maria ...
- 第00章—IDEA
spring boot 系列学习记录:http://www.cnblogs.com/jinxiaohang/p/8111057.html 码云源码地址:https://gitee.com/jinxia ...
- Python(数据库安装与基本语句)
一.数据库相关概念 1.两种硬件扩展方式 a.垂直扩展:针对一台计算机 b.水平扩展:多台普通计算机 2.数据库相关概念 数据库服务器(本质就是一个台计算机,该计算机之上安装有数据库管理软件的服务端) ...
- HTML(form标签)、CSS
一.表单标签<form> 功能:表单用于向服务器传输数据,从而实现用户与Web服务器的交互. 表单能够包含input系列标签,比如文本字段.复选框.单选框.提交按钮等等. 表单还可以包含t ...
- Visual Studio 正则表达式替换
查找空行:^(?([^\r\n])\s)*\r?$\r?\n 查找多余的空行:^(?([^\r\n])\s)*\r?$\r?\n^(?([^\r\n])\s)*\r?$\r?\n
- smarty基础原理
smarty基础原理 一.html模板页面 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &q ...
- java中文转Unicode
public String cnToUnicode(String cn) { char[] chars = cn.toCharArray(); String returnStr = "&qu ...
- 开发者需要知道的iOS 12
总体概况 iOS 12总体来看是对现有iOS的一次改进,并没有太多突破性的功能或者框架,但是Apple在底层做了很多优化的工作,优化了性能,提供了更强大的安全性,增强了AR.Siri体验,让人工智能更 ...
- npm国内镜像设置
http://cnodejs.org/topic/4f9904f9407edba21468f31e