1. 程式人生 > 實用技巧 >SpringBoot使用AOP(動態代理)

SpringBoot使用AOP(動態代理)

SpringBoot使用AOP(動態代理)

  • cgllib 需要新增依賴
<!--cglib動態代理-->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>
<dependency>
      <groupId>org.springframework.boot</groupId
> <artifactId>spring-boot-starter-aop</artifactId> <version>2.1.7.RELEASE</version> </dependency>

  1. 目標target(需要被增強的介面)
//目標target
public interface UserService {
    void login(String username, String password);
    void regist();
    void search();
    void
update(); }

  1. 介面實現
/**
 * @Description
 * @Author zhoumm
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2019-08-24
 */
@Service
public class UserServiceImpl implements UserService{
    @Override
    public void login(String username, String password) {
        System.out.println ("登入");
    }

    @Override
    
public void regist() { System.out.println ("註冊"); } @Override public void search() { System.out.println ("userService search..."); } @Override public void update() { System.out.println ("update..."); } }

  1. 代理增強類
/**
 * @Description  代理增強類
 * @Author 
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2019-08-25
 */
@Component
@Aspect  //標識為一個切面供容器讀取
public class UserServiceHelper {
    @Before("execution(* com.mmz.tkp.controller.aoptest.UserService.s*(..))")
    public void before(){
        System.out.println ("前置通知。。。");
    }

    @AfterReturning(value="execution(* com.mmz.tkp.controller.aoptest.UserService.s*(..))")
    public void afterReturning(){
        System.out.println ("後置通知。。。");
    }

    @Around("execution(* com.mmz.tkp.controller.aoptest.UserService.s*(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println ("環繞前。。。");
        Object value = pjp.proceed();
        System.out.println (value);
        System.out.println ("環繞後。。。");
    }

    @After("execution(* com.mmz.tkp.controller.aoptest.UserService.s*(..))")
    public void after(){
        System.out.println ("最終通知。。。");
    }

    @AfterThrowing(value="execution(* *.s*(..))",throwing="ex")
    public void afterThrowing(JoinPoint jp,Throwable ex){
        System.out.println ("異常丟擲通知" + ex);
    }

    @Pointcut("execution(* com.mmz.tkp.controller.aoptest.UserService.search(..))")
    public void mypointcut(){}

    @Pointcut("execution(* com.mmz.tkp.controller.aoptest.UserService.update(..))")
    public void mypointcut1(){}

    //使用@Pointcut來宣告切點,避免在每個通知中定義切點
    @Before("mypointcut()||mypointcut1()")
    public void before1(){
        System.out.println ("前置通知。。。。");
    }
}

  1. JDK動態代理工廠類
/**
 * @Description JDK動態代理工廠類
 * 在執行期 ,在JVM內部動態生成class位元組碼物件(Class物件)
 * Jdk動態代理只針對於介面操作
 * @Author 
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2019-08-24
 */
public class JDKProxyFactory implements InvocationHandler {
    //目標物件
    private Object target;

    public JDKProxyFactory(Object target) {
        this.target = target;
    }

    //使用Proxy建立代理物件
    public Object createProxy(){
        //目標類的類載入器物件
        ClassLoader classLoader = target.getClass().getClassLoader();
        //目標類的實現介面的Class[]
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //當前物件需實現InvocationHandler介面
        return Proxy.newProxyInstance(classLoader,interfaces,this);
    }

    /**
     *
     * @param proxy  代理物件,一般不用
     * @param method 方法物件
     * @param args  方法引數
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在呼叫目標物件方法前,統一做一些其他操作,即功能增強
        System.out.println ("例如:日誌操作......");
        return method.invoke(target,args);
    }
}

  1. CGLIB動態代理
/**
 * @Description  CGLIB動態代理
 * CGLIB(Code Generation Library)是一個開源專案
 * 是一個強大的,高效能,高質量的Code生成類庫,它可以在執行期擴充套件Java類與實現Java介面。
 * CGLIB包的底層是通過使用一個小而快的位元組碼處理框架ASM,來轉換位元組碼並生成新的類
 *
 * 可以為沒有實現介面的類去做代理,也可以為實現介面的類去做代理。
 *
 * spring採用的是哪一種動態機制:
 *     如果目標物件,有介面,優先使用jdk動態代理
 *     如果目標物件,無介面,使用cglib動態代理。
 * @Author zhoumm
 * @Version V1.0.0
 * @Since 1.0
 * @Date 2019-08-24
 */
public class CglibProxyFactory implements MethodInterceptor {
    //目標物件
    private Object target;

    public CglibProxyFactory(Object target) {
        this.target = target;
    }

    //建立代理物件
    public Object createProxy(){
        //1.建立Enhancer
        Enhancer enhancer = new Enhancer();
        //2.傳遞目標物件Class
        enhancer.setSuperclass(target.getClass());
        //3.設定回撥操作(相當於InvocationHandler)
        enhancer.setCallback(this);

        return enhancer.create();
    }


    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //在呼叫目標物件方法前,統一做一些其他操作,即功能增強
        System.out.println ("例如:日誌操作......");
        return method.invoke(target,args);
    }
}

  1. controller 代理測試
@Api(value = "aop", description = "面向切面程式設計")
@RestController
@RequestMapping("/kpt/aop")
@Slf4j
@Validated
public class AopBackGroundController {

    @Autowired
    private UserService userService;

    @ApiOperation(value = "JDK動態代理測試", httpMethod = "GET")
    @RequestMapping(value = "/jdkproxy", method = RequestMethod.GET)
    public void testJDKProxy(){
        //JDK動態代理
        userService.login("zz","123456");
        System.out.println ("--------------JDK動態代理------------");
        JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(userService);
        UserService userServiceProxy = (UserService)jdkProxyFactory.createProxy();
        userServiceProxy.login("zz","123456");
    }

    @ApiOperation(value = "CGLIB動態代理測試", httpMethod = "GET")
    @RequestMapping(value = "/cglibproxy", method = RequestMethod.GET)
    public void testCGLIBProxy(){
        //CGLIB動態代理
        userService.regist();
        System.out.println ("--------------CGLIB動態代理------------");
        CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(userService);
        UserService userServiceProxy = (UserService)cglibProxyFactory.createProxy();
        userServiceProxy.regist();
    }

    @ApiOperation(value = "AspectJ代理測試", httpMethod = "GET")
    @RequestMapping(value = "/aspectJproxy", method = RequestMethod.GET)
    public void testAspectJ(){
        //com.mmz.tkp.controller.aoptest.UserServiceHelper
        userService.search();
    }

    @ApiOperation(value = "切點通知測試", httpMethod = "GET")
    @RequestMapping(value = "/aspectJproxy", method = RequestMethod.GET)
    public void testPointcutAdvice(){
        //com.mmz.tkp.controller.aoptest.UserServiceHelper
        userService.search();
    }
}