spring----AOP 和 AspectJ
动态代理:
代理只能为类生成代理,不能为方法生成代理,如果需要为指定的方法代理,必须只能对该对象生成代理,然后在invoke方法中,判断方法是不是我们要代理的方法(使用注解注解到对应的方法上)
需要分清楚,代理类,目标类,和切面类
代理类中直接调用的切面对象是切面类。我们可以继承切面类,写自己的子类,实现切面类中的某些方法。但是切面方法(由代理类调用)我们一般是不能重写的。
AOP介绍:
什么是AOP
AOP采用了横向抽取机制取代了传统的纵向继承体系重复性代码
经典应用:事务管理,缓存,性能监控,安全检查
AOP实现原理
实现原理:底层采用代理机制来实现的。
接口+实现类:spring采用jdk的动态代理Proxy
只有实现类,spring采用cglib字节码增强
AOP术语
target:目标对象,需要被代理的对象,例如UserService
Joinpoint:连接点,指可能被拦截到的方法,例如UserService中的所有的方法
PointCut:切入点,已经被增强的连接点,例如UserService中的save() 方法
Aspect:切面,切入点和通知的结合,交叉在各个业务逻辑中的系统服务,类似于安全验证,事务处理,日志记录都可以理解为切面
advice:通知/增强,切面的实现,切面织入的目标对象,时间点(方法执行前,后,前后,异常)以及内容
Weaving:织入,将切面代码插入到目标对象某一个方法的过程,相当于我们在jdk动态代理里面的invocationHandler接口方法中的内容
proxy:代理,一个类被AOP织入增强后,就产生了一个结果代理类
代理类
所有的调用的方法,有代理类来完成。代理类会拦截所有的方法,通过代理类来调用相应的方法,并在调用方法前后,执行其他的操作,在使用上还是调用接口中方法。这个可以参考python中的装饰器,大同小异。
手动创建代理类:
为某一个特定的类,创建代理
JDK动态代理
jdk动态代理是对"装饰者"设计模式的简化
service
public interface UserService { void save(); void delete(); }
serviceImpl
public class UserServiceImpl implements UserService { @Override public void save() { System.out.println("保存数据完毕"); } @Override public void delete() { System.out.println("删除数据完毕"); } }
MyAspect
public class MyAspect { public void before() { System.out.println("开启事务"); } public void after() { System.out.println("结束事务"); } }
MybeanFactory(生成代理对象)
public class MyBeanFactory { public static UserService createUserService(){ //目标类 UserService userService = new UserServiceImpl(); //切面类 MyAspect myAspect = new MyAspect(); /** * 参数1:loader,类加载器,动态代理类运行时创建,任何类都需要类加载器将类加载到内存中 * 一般写法, 当前类.class.getClassLoader() * 参数2:interfaces,代理类需要实现的所有的接口 * 写法1,目标类实例对象.getClass().getInterfaces(),只能获得自己类的所有的接口,不能获得父类的接口 * 写法2,new Class[]{目标类接口.class} * 参数3:invocationHandle,处理类,接口,一般采用匿名内部类实例化接口 * 提供了一个invoke方法,代理类的每一个方法执行的时候,都会调用invoke方法; */ UserService proxy = (UserService) Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(), new Class[]{UserService.class}, new InvocationHandler() { /** * * @param proxy:代理对象 * @param method:代理对象当前执行方法的描述对象(反射) * method.getName():方法名 * method.invoke(目标类对象,args) * @param args:方法传递的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { myAspect.before(); Object obj = method.invoke(userService,args); myAspect.after(); return obj; } }); return proxy; } }
测试
public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { UserService userService = MyBeanFactory.createUserService(); userService.save(); userService.delete(); } }
cglib字节码增强
没有接口,只有实现类
需要导入核心的两个jar包(sam,cglib),但是spring-core已经包含了这两个jar包
UserServiceImpl
public class UserServiceImpl{ public void save() { System.out.println("保存数据完毕"); } public void delete() { System.out.println("删除数据完毕"); } }
MyAspect
public class MyAspect { public void before() { System.out.println("开启事务"); } public void after() { System.out.println("结束事务"); } }
MyBeanFactory
public class MyBeanFactory { public static UserServiceImpl createUserService(){ UserServiceImpl userServiceImpl = new UserServiceImpl(); MyAspect myAspect = new MyAspect(); /** * 代理类,采用cglib,底层创建目标类的子类 */ //核心类 Enhancer enhancer = new Enhancer(); //确定父类 enhancer.setSuperclass(userServiceImpl.getClass()); //设置回调 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { myAspect.before(); //执行目标类的方法 Object obj = method.invoke(userServiceImpl,args); //methodProxy.invokeSuper(proxy,args); 等同上面的代码(执行代理类父类的方法,即执行目标类) myAspect.after(); return obj; } }); //创建代理 UserServiceImpl userServiceProxy = (UserServiceImpl)enhancer.create(); return userServiceProxy; } }
测试
public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { //手动创建代理 UserServiceImpl userServiceimpl = MyBeanFactory.createUserService(); userServiceimpl.save(); userServiceimpl.delete(); } }
JDK动态代理和Cglib代理
JDK动态代理生成的代理类并不是被代理类的子类,它只是实现了被代理类对应接口的一个匿名类,所以接口中没有的方法是不可用的,而且代理无法使用被代理类的变量
但CGLIB确是被代理类的子类,代理类可以直接使用被代理类的变量。
interface A{ public void run(); } public class Demo { @Test public void A(){ A a = (A)Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{A.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args){ //可以没有实现类 System.out.println("代理"); return proxy; } }); a.run(); } }
spring框架配置代理(半自动):
为指定的类配置代理
UsersService
public interface UserService { void save(); void delete(); }
UserServiceImpl
public class UserServiceImpl implements UserService{ public void save() { System.out.println("保存数据完毕"); } public void delete() { System.out.println("删除数据完毕"); } }
MyAspect (切面类,可以理解拦截器)
AOP联盟通知类型
前置通知 MethodBeforeAdvice:在目标方法执行前实施增强
后置通知 AfterReturningAdvice:在目标方法执行后实施后增强
环绕通知 MethodInterceptor :在目标方法执行前后实施增强
异常抛出通知 ThrowsAdvice:在方法抛出异常后实施增强
引介通知 IntroductionInterceptor:在目标类中添加一些新的方法和属性
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyAspect implements MethodInterceptor { public static void before() { System.out.println("开启事务"); } public static void after() { System.out.println("结束事务"); } @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { MyAspect.before(); Object obj = methodInvocation.proceed(); MyAspect.after(); return obj; } }
xml配置可以简单理解,配置了接口和实现类,表示调用的目标对象,配置了切面类(拦截器),拦截方法的调用(拦截后,可以选择处理方法,也可以忽略方法)
xml配置取代了手动编写代理(jdk动态代理)中的MyBeanFactory,由spring来生成动态代理.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 目标类--> <bean id="userServiceImpl" class="com.test.UserServiceImpl"></bean> <!-- 切面类--> <bean id="myAspect" class="com.test.MyAspect"></bean> <!-- 创建代理类 使用工厂 factory bean,底层调用getObject(),返回特殊的Bean ProxyFactoryBean 用于创建工厂bean,生成特殊的代理对象 interfaces:确定接口 如果只有一个接口 可以简写<property name="interfaces" value=""> 如果有多个接口,就使用<array> 底层: 如果有接口,默认采用jdk动态代理 如果没有接口,应该采用cglib(但是测试该是使用的是jdk动态代理) 如果加上 <property name="optimize" value="true"></property>,表示一定是采用了cglib字节码增强 --> <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces"> <array> <value>com.test.UserService</value> </array> </property> <property name="target" ref="userServiceImpl"></property> <property name="interceptorNames" value="myAspect"></property> <!--<property name="optimize" value="true"></property>--> </bean> </beans>
测试
public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); //有spring创建代理 UserService userServiceproxy = (UserService) classPathXmlApplicationContext.getBean("proxy"); userServiceproxy.save(); userServiceproxy.delete(); } }
spring框架配置代理(全自动,重点):
扫描一个包下/一个类下所有的方法/指定的方法,为这些方法进行代理
从spring容器中获得对象,如果配置了aop,将自动生成代理
导包,aspectj
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
这个包也可以com.springsource.org.aspectj.weaver-1.7.2.RELEASE.jar,但是maven下载不下来。可以手动添加
UserService
public interface UserService { void save(); void delete(); }
UserServiceImpl
public class UserServiceImpl implements UserService{ public void save() { System.out.println("保存数据完毕"); } public void delete() { System.out.println("删除数据完毕"); } }
MyAspect
public class MyAspect implements MethodInterceptor { public static void before() { System.out.println("开启事务"); } public static void after() { System.out.println("结束事务"); } @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { MyAspect.before(); Object obj = methodInvocation.proceed(); MyAspect.after(); return obj; } }
xml
将<aop:config>注释掉,就直接恢复正常的调用了
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目标类--> <bean id="userServiceImpl" class="com.test.UserServiceImpl"></bean> <!-- 切面类--> <bean id="myAspect" class="com.test.MyAspect"></bean> <!-- aop编程 导入命名空间(xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd") 使用<aop:config>进行配置 默认使用jdk动态代理 如果添加上 proxy-target-class="true",使用cglib代理 <aop:pointcut> 切入点:从目标对象获取具体方法 expression:里面写切入点表达式 格式:execution(* com.test.UserServiceImpl.*(..)) * 表示所有的方法返回值任意 * 表示类中的所有的方法 (..) 表示方法中的传递参数任意 修改:execution(* com.test.*.*(..)) 将类名变成 * :表示所有的类 <aop:advisor> 特殊的切面:只有一个通知和一个切入点 advice-ref:配置通知 pointcut-ref:配置切入点 --> <aop:config proxy-target-class="true"> <aop:pointcut expression="execution(* com.test.UserServiceImpl.*(..))" id="myPointCut"></aop:pointcut> <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor> </aop:config> </beans>
测试
public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserService userService = (UserService) classPathXmlApplicationContext.getBean("userServiceImpl"); userService.save(); userService.delete(); } }
spring框架使用Aspectj:
aspectj是一个基于java语言的aop框架
切入点表达式
1、execution() :用于描述方法
语法:execution(修饰符 返回值 包.类.方法(参数) throws 异常) 修饰符;一般省略 public:公共方法 * :任意 返回值: Void :没有返回值 String :返回字符串 * :返回任意 包,[可以省略] com.itheima.crm 固定包 com.itheima.crm.*.service crm包下面子包任意 (例如:com.itheima.crm.staff.service) com.itheima.crm.. crm包下面的所有子包(含自己) com.itheima.crm.*.service.. crm包下面任意子包,固定目录service,service目录任意包 类,[可以省略] UserServiceImpl 指定类 *Impl 以Impl结尾 User* 以User开头 * 任意 方法名,不能省略 addUser 固定方法 add* 以add开头 *Do 以Do结尾 * 任意 (参数) () 无参 (int) 一个整型 (int ,int) 两个 (..) 参数任意 throws ,可省略,一般不写。
示例
execution(* com.itheima.crm.*.service..*.*(..)) <aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) || execution(* com.itheima.*Service.*(..))" id="myPointCut"/>
2、within:匹配包或子包中的方法(了解)
within(com.itheima.aop..*)
3、this:匹配实现接口的代理对象中的方法(了解)
this(com.itheima.aop.user.UserDAO)
4、target:匹配实现接口的目标对象中的方法(了解)
target(com.itheima.aop.user.UserDAO)
5、args:匹配参数格式符合标准的方法(了解)
args(int,int)
6、bean(id) 对指定的bean所有的方法(了解)
bean('userServiceId')
AspectJ 通知类型
aop联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。
aspectj 通知类型,只定义类型名称。以及方法格式。
个数:6种,知道5种,掌握1中。
before:前置通知(应用:各种校验) 在方法执行前执行,如果通知抛出异常,阻止方法运行 afterReturning:后置通知(应用:常规数据处理) 方法正常返回后执行,如果方法中抛出异常,通知无法执行 必须在方法执行后才执行,所以可以获得方法的返回值。 around:环绕通知(应用:十分强大,可以做任何事情) 方法执行前后分别执行,可以阻止方法的执行 必须手动执行目标方法 afterThrowing:抛出异常通知(应用:包装异常信息) 方法抛出异常后执行,如果方法没有抛出异常,无法执行 after:最终通知(应用:清理现场) 方法执行完毕后执行,无论方法中是否出现异常
导入jar包
aop联盟规范、spring aop 实现、aspect 规范、spring aspect 实现 spring-aspects.jar
在maven中,只需要依赖sprint-context和aspectjweaver这连个jar包即可测试
基于xml使用方法
UserService
public interface UserService { void save(); void delete(); }
UserviceImpl
public class UserServiceImpl implements UserService{ public void save() { //int a = 1/0; //测试抛出异常 System.out.println("保存数据完毕"); } public void delete() { System.out.println("删除数据完毕"); } }
MyAspect
不在实现MethodInterceptor等接口了
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; public class MyAspect{ /** * * @param joinPoint:通知方法可以加上参数,用于描述连接点(目标方法) */ public static void before(JoinPoint joinPoint) { System.out.println("前置通知--method="+joinPoint.getSignature().getName()); } /** * * @param ret 方法的返回值,参数名字(ret)必须和xml配置中的一样[<aop:after-returning method="after" pointcut="execution(* com.test.UserServiceImpl.save(..))" returning="ret"></aop:after-returning>] */ public static void afterReturning(JoinPoint joinPoint,Object ret) { System.out.println("后置通知--method="+joinPoint.getSignature().getName()+"--返回值="+ret); } public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //需要手动执行目标方法;会抛出异常 Object object = proceedingJoinPoint.proceed(); System.out.println("环绕通知--method="+proceedingJoinPoint.getSignature().getName()); return object; } /** * * @param e:异常对象,参数名(e)由xml配置[<aop:after-throwing method="throwing" pointcut-ref="myPointCut" throwing="e" />] */ public void throwing(Throwable e){ System.out.println("异常通知="+e.getMessage()); } public void after(JoinPoint joinPoint){ System.out.println("最终通知,method方法:"+joinPoint.getSignature().getName()+"结束"); } }
xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目标类--> <bean id="userServiceImpl" class="com.test.UserServiceImpl"></bean> <!-- 切面类--> <bean id="myAspect" class="com.test.MyAspect"></bean> <!-- aop编程 <aop:aspect></aop:aspect> :将切面类声明"切面",从而获得通知方法; ref:切面类引用 <aop:pointcut>声明一个切入点,所有的通知都可以使用 id:用于其他通知引用 execution():切入点表达式 --> <aop:config> <aop:aspect ref="myAspect"> <!-- <aop:pointcut> 切入点,可以给所有通知使用; --> <aop:pointcut expression="execution(* com.test.UserServiceImpl.*(..))" id="myPointCut"></aop:pointcut> <!-- method:通知方法 pointcut:切入点表达式,此表达式只能当前通知使用 pointcut-ref:切入点引用,可以与其他通知共享切入点; --> <aop:before method="before" pointcut-ref="myPointCut"></aop:before> <!-- 后置方法可以获得方法的返回值 --> <aop:after-returning method="afterReturning" pointcut="execution(* com.test.UserServiceImpl.save(..))" returning="ret"></aop:after-returning> <!-- 环绕通知 --> <aop:around method="around" pointcut="execution(* com.test.UserServiceImpl.delete(..))"></aop:around> <!-- 异常通知 --> <aop:after-throwing method="throwing" pointcut-ref="myPointCut" throwing="e"></aop:after-throwing> <!-- 最终通知 --> <aop:after method="after" pointcut-ref="myPointCut"></aop:after> </aop:aspect> </aop:config> </beans>
测试
public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserService userService = (UserService) classPathXmlApplicationContext.getBean("userServiceImpl"); userService.save(); userService.delete(); } }
基于注解使用方法
UserService
public interface UserService { void save(); void delete(); }
UserServiceImpl
@Service("userServiceImpl") public class UserServiceImpl implements UserService{ public void save() { //int a = 1/0; //测试抛出异常 System.out.println("保存数据完毕"); } public void delete() { System.out.println("删除数据完毕"); } }
MyAspect(切面类)
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; //@Aspect 声明切面类 @Component @Aspect public class MyAspect{ //配置公共的切入点; @Pointcut("execution(* com.test.UserServiceImpl.*(..)))") private void myPointCut(){ } /** * * @param joinPoint:通知方法可以加上参数,用于描述连接点(目标方法) */ @Before("execution(* com.test.UserServiceImpl.*(..))") public static void before(JoinPoint joinPoint) { System.out.println("前置通知--method="+joinPoint.getSignature().getName()); } /** * * @param ret 方法的返回值,参数名字(ret)必须和xml配置中的一样[<aop:after-returning method="after" pointcut="execution(* com.test.UserServiceImpl.save(..))" returning="ret"></aop:after-returning>] */ @AfterReturning(value = "execution(* com.test.UserServiceImpl.save(..))",returning = "ret") public static void afterReturning(JoinPoint joinPoint,Object ret) { System.out.println("后置通知--method="+joinPoint.getSignature().getName()+"--返回值="+ret); } @Around("execution(* com.test.UserServiceImpl.delete(..))") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //需要手动执行目标方法;会抛出异常 Object object = proceedingJoinPoint.proceed(); System.out.println("环绕通知--method="+proceedingJoinPoint.getSignature().getName()); return object; } /** * * @param e:异常对象,参数名(e)由xml配置[<aop:after-throwing method="throwing" pointcut-ref="myPointCut" throwing="e" />] */ @AfterThrowing(value = "execution(* com.test.UserServiceImpl.save(..))",throwing = "e") public void throwing(Throwable e){ System.out.println("异常通知="+e.getMessage()); } @After(value = "myPointCut()") public void after(JoinPoint joinPoint){ System.out.println("最终通知,method方法:"+joinPoint.getSignature().getName()+"结束"); } }
xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.test"></context:component-scan> <!-- 确定aop注解生效(Aspect) --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
测试
public class Test{ public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("spring-context.xml"); UserService userService = (UserService) classPathXmlApplicationContext.getBean("userServiceImpl"); userService.save(); userService.delete(); } }
spring Boot 整合AspectJ:
使用 @EnableAspectJAutoProxy 代替了配置文件
@SpringBootApplication @EnableAspectJAutoProxy public class SpringbootProjectApplication { public static void main(String[] args) { SpringApplication.run(SpringbootProjectApplication.class, args); } }
手写自动代理底层实现:
思路:bean在被注册到容器之前,我们对bean进行处理,生成bean的代理对象,然后注册到spring容器中。这样我们从spring容器获取的对象就是代理对象了。
解决:
- 利用bean的生命周期
- 首先,我们需要实现一个接口,也就是BeanPostProcessor接口。BeanPostProcessor接口作用是:如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。
- 而我们想要在原型对象bean被创建之后就代理了,就必须在原来的容器中拿到原来的原型对象,需要拿到原来spring容器中的切面对象,这个时候,我们就需要原来的容器,这个时候就需要另一个接口,也就是ApplicationContextAware接口!
Bean
public class Persion { public void run(){ System.out.print("跑"); } }
切面类
//环绕通知 MethodInterceptor :在目标方法执行前后实施增强 public class MyAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { //执行方法前执行 Object obj = arg0.proceed(); //执行方法后执行 return obj; } }
MyAutoProxy
将bean的代理对象注入到spring容器中
public class MyAutoProxy implements BeanPostProcessor,ApplicationContextAware{ private ApplicationContext applicationContext=null; //bean创建之前调用 @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean;//在这里,我们直接放行 } //bean创建之后调用 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { ProxyFactoryBean factory = new ProxyFactoryBean(); factory.setTarget(bean); Advisor adv = applicationContext.getBean("MyAdvisor"); factory.addAdvisor(adv); //返回bean的代理对象 return factory.getObject(); } //拿到原来的spring中的容器 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } }
xml
<bean id="person" class="com.joyin.ticm.zy.Persion"></bean> <bean class="com.joyin.ticm.zy.MyAutoProxy"></bean> <bean id="MyAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 切入点,被增强的方法--> <property name="patterns"> <list> <value>.*run.*</value> </list> </property> <!-- 通知-由我们写,实际代理动作 --> <property name="advice"> <bean id="advice" class="com.joyin.ticm.zy.MyAdvice"></bean> </property> </bean>
测试
ApplicationContext ctx = new ClassPathXmlApplicationContext("file:src/main/config/spring/applicationContext.xml"); Persion persion = ctx.getBean(Persion.class); persion.run();
但是这种方式生成的bean不能被spring自动注入,没有测过使用接口注入。
报错:
Bean named 'person' must be of type [com.joyin.ticm.zy.Persion], but was actually of type [$Proxy16]
@Resource(name="person") Persion persion;
spring aop的底层原理:
spring aop的底层实现,主要是通过cglibg的动态代理和责任链模式完成。
我们在编写aop,将生产代理类和通知耦合在一起了,非常不好