Spring AOP

函数增强,当前有二种处理方式,

a. 基于jdk的接口动态代理增强

b. 基于 cglib 的父类动态代理增强

在Spring框架中,在动态代理增加时,依据是否有接口来自动识别并实现增强

如果有接口,将使用基于 jdk的接口动态代理,否则使用基于cglib的父类动态代理增强;

 

01. 基于 jdk 动态代理增强

  >> 要求: 必须有接口;否则无法实现动态代理;

  >> 代码实现

1 //接口
2 public interface TargetInterface {
3     public void save();
4 }
1 //接口实现
2 public class Target implements TargetInterface {
3     @Override
4     public void save() {
5         System.out.println("目标方法执行 ... ");
6     }
7 }
 1 //增强对象
 2 public class Advice {
 3 
 4     public void preAdvice(){
 5         System.out.println("前置增强...");
 6     }
 7 
 8     public void sufAdvice(){
 9         System.out.println("后置增强...");
10     }
11 }

 

 1 //动态代理增强
 2     public static void main(String[] args) {
 3         //目标对象
 4         Target target = new Target();
 5         //增强对象
 6         Advice advice = new Advice();
 7         //jdk的接口代理增强
 8         TargetInterface proxyInstance = (TargetInterface) Proxy.newProxyInstance(
 9                 target.getClass().getClassLoader(),
10                 target.getClass().getInterfaces(),
11                 new InvocationHandler() {
12                     @Override
13                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14                         advice.preAdvice();
15                         Object invoke = method.invoke(target, args);
16                         advice.sufAdvice();
17                         return invoke;
18                     }
19                 }
20         );
21         proxyInstance.save();
22     }

 

02.基于cglib的动态代理增强

  >> 无接口要求

  >> 代码实现 (增强对象与上面一样)

1 //目标对象  
2 public class Target  {
3     public void save() {
4         System.out.println("目标方法执行 ... ");
5     }
6 }

 

 1 //动态代理增强
 2     public static void main(String[] args) {
 3         //目标对象
 4         Target target = new Target();
 5         //增强对象
 6         Advice advice = new Advice();
 7         //cglib增强对象
 8         Enhancer enhancer = new Enhancer();
 9         //设置父类
10         enhancer.setSuperclass(target.getClass());
11         //设置回调
12         enhancer.setCallback(new MethodInterceptor() {
13             @Override
14             public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
15                advice.preAdvice();
16                 Object invoke = method.invoke(target, args);
17                 advice.sufAdvice();
18                 return invoke;
19             }
20         });
21         //生成代理对象
22         Target o = (Target) enhancer.create();
23         o.save();
24 
25     }

 

03. 基于Spring 配置实现动态代理增强  (目标对象带接口)

 1 // applicationContext.xml 配置
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xmlns:aop="http://www.springframework.org/schema/aop"
 5        xsi:schemaLocation="
 6        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 7        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
 8 ">
 9 
10     <!--目标对象(带接口)-->
11     <bean id="target" class="club.wuyu.proxy.AOP.Target"/>
12     <!--目标对象(无接口)-->
13     <bean id="targetcglib" class="club.wuyu.proxy.AOP.TargetCglib"/>
14     <!--切面对象-->
15     <bean id="advice" class="club.wuyu.proxy.AOP.Advice"/>
16 
17     <!--织入-->
18     <aop:config>
19         <aop:aspect ref="advice">
20             <aop:before method="preAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.Target.save())"/>
21             <aop:after method="sufAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.Target.save())"/>
22 
23             <aop:before method="preAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.TargetCglib.save())"/>
24             <aop:after method="sufAdvice" pointcut="execution(public void club.wuyu.proxy.AOP.TargetCglib.save())"/>
25         </aop:aspect>
26     </aop:config>
27 </beans>
//测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPTest {

    @Autowired
    private TargetInterface targetInterface;

    @Autowired
    private TargetCglib targetCglib;

    @Test
    public void test02(){
        targetCglib.save();
    }

    @Test
    public void test01(){
        targetInterface.save();
    }
}

 

04. AOP配置说明

  

 

  

 

   >> 环绕方法的配置

     <aop:around method="around" pointcut="execution(* com.itheima.aop.*.*(..))"/> 

    

   >> 切点表达式的抽取, 便于多个不同的切点类型引用切点表达式;

    切点表达式的引用:pointcut-ref 

05. 关于注解方式动态代理的实现

  >> 创建目标接口及目标类 (内部有目标方法);   

  >> 创建切面类 (内部有增强方法) 

  >> 将目标类和切面类做注解申明,将对象的创建权交由Spring; @Componet 注解;

    >> 在切面类中使用注解,配置织入关系   

 1 @Component("advice")    //切面类的创建权,交由Spring
 2 @Aspect                         //标识当前类为一个切面类
 3 public class Advice {
 4 
 5     @Before("execution(* club.wuyu.proxy.anno.*.*(..))") //织入
 6     public void preAdvice(){
 7         System.out.println("前置增强...");
 8     }
 9     @After("execution(* club.wuyu.proxy.anno.*.*(..))") //织入
10     public void sufAdvice(){
11         System.out.println("后置增强...");
12     }
13 }

   >> 配置文件

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xmlns:context="http://www.springframework.org/schema/context"
 5        xmlns:aop="http://www.springframework.org/schema/aop"
 6        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 7        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
 8        http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
 9 
10     <context:component-scan base-package="club.wuyu.proxy.anno"/>
11 
12     <aop:aspectj-autoproxy proxy-target-class="true"/>
13     <!--<aop:aspectj-autoproxy/>-->
14 
15 </beans>

 

  <aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspect 切面的bean创建代理,织入切面。

  <aop:aspectj-autoproxy />的proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强。

  当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。

  如果proxy-target-class设置为false,但是目标类没有声明接口,则spring将自动使用CGLib动态代理。

  >> 关于 but was actually of type 'com.sun.proxy.$Proxy20' 错误的原因及处理

  >> 在 java 中,默认使用的动态代理是JDK基于接口的代理,如果出现上述错误,极有可能是在自动注入的时候,注入到了目标对象的实现类上,而不是目标对象的接口上;

   即:配置  <aop:aspectj-autoproxy/>

           或:配置  <aop:aspectj-autoproxy proxy-target-class="false"/>   时

      自动注入配置到了实现类上

 1 @RunWith(SpringJUnit4ClassRunner.class)
 2 @ContextConfiguration("classpath:applicationContext-anno.xml")
 3 public class AnnoTest {
 4 
 5     @Autowired
 6     private Target target;
 7 
 8     @Test
 9     public void Test01(){
10         target.save();
11     }
12 }

 

  正确的注入,应该是接口

1     @Autowired
2     private TargetInterface target;

 

       或者,直接配置为 CGLib 动态代理, 

  即:<aop:aspectj-autoproxy proxy-target-class="true"/> 

 

  >> 注解方式下,切点表达式的抽取

    

 

 

    

 

posted @ 2022-06-13 10:31  耗喜天涯  阅读(22)  评论(0编辑  收藏  举报