代理可以在不改变源代码的基础上实现对代码的拓展
常用到的业务场景:
业务类
/**
* 业务层接口
*/
public interface UserService {
void addUser();
void updateUser();
void deleteUser();
String getUser();
}
业务层中的具体实现类
package com.atguigu.service.impl;
import com.atguigu.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("用户新增成功");
}
@Override
public void updateUser() {
System.out.println("修改用户成功");
}
@Override
public void deleteUser() {
System.out.println("删除用户成功");
}
@Override
public String getUser() {
return "MrCui";
}
}
代理类:
/**
* 静态代理
*/
public class StaticProxy {
private static UserService userService = new UserServiceImpl();
// 代理方法
public static void addUser() {
try {
System.out.println("1-开启事务 ");
userService.addUser();
System.out.println("2-提交事务");
} catch (Exception e) {
System.out.println("3-回滚事务");
e.printStackTrace();
throw new RuntimeException(e);
} finally {
System.out.println("4-关闭事务");
}
}
}
静态代理类需要手动的写业务层中的每一个方法,会导致开发效率太低,代码冗余。为了解决这一痛点,出现了动态代理。
目前动态代理有两种实现:
注意事项及原理:
通过JDK的动态代理来实现:
public class MyJDKProxy {
/**
*
* @param target 需要被代理的目标对象
* @return
*/
public static Object proxy(Object target){
// 通过反射创建目标对象的代理对象,
// 参数分别是
// 类加载器
// 目标对象所实现的接口
// new InvocationHandler() 匿名内部类 这里面写的是具体的业务逻辑
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
Object result = null;
try {
System.out.println("开启事务");
// 可以在这里开启暴力反射
result = method.invoke(target,args);
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
throw new RuntimeException();
}finally {
System.out.println("关闭事务");
}
return result;
});
}
}
注意事项及原理:
public class MyCglibProxy {
public static Object proxy(Object target){
Enhancer enhancer = new Enhancer();
// 获取目标类所实现的接口并设置代理类的接口
enhancer.setInterfaces(target.getClass().getInterfaces());
// 设置代理类的父类 即目标类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback((InvocationHandler) (proxy, method, args) -> {
Object result = null;
try {
System.out.println("开启事务");
result = method.invoke(target,args);
System.out.println("提交事务");
}catch (Exception e){
System.out.println("回滚事务");
e.printStackTrace();
throw new RuntimeException(e);
}finally {
System.out.println("关闭事务");
}
return result;
});
// 创建代理对象
return enhancer.create();
}
}
通过上述的操作我们已经可以实现了动态代理,但是目前还是有一些问题:
首先开发效率太低,其次我们无法来进行细节的处理。此时Spring官网为我们提供了代理即AOP
我们可以通过AOP来实现更好的动态代理。
在学习Spring的动态代理之前首先我们需要了解一个关键词,这些关键词是Spring官方提供给我们,
帮我我们更好的理解Spring如何实现AOP规则的。
通知方式:
步骤:
第一步: 首先需要引入依赖
<dependencyes>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.20version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.20version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencyes>
第二步: 编写AOP来实现动态代理
/**
* 基于Spring的Aop的动态代理。
* 基于Spring的Aop主要有5中通知
* 1.before 前置通知, 方法执行之前进行通知
* 2.afterReturning 返回通知, 方法执行后但还为返回结果此时进行通知
* 3.afterThrowing 异常通知, 方法执行时出现了异常则进行此通知。
* 4.after 后置通知 方法执行之后进行通知
* 5.around 环绕通知, 环绕通知可以控制目标方法是否调用。(最常用的通知)
*/
@Component
@Aspect
public class AopProxy {
/**
* 作用是用于标记
*/
@Pointcut("bean(userServiceImpl)")
public void pointCut(){}
/**
* 前置通知
*/
@Before("pointCut()")
public void before(){
System.out.println("1-前置通知");
}
@AfterReturning(value = "pointCut()",returning = "result")
public void afterReturning(Object result){
System.out.println("2-返回通知"+result);
}
@AfterThrowing(value = "pointCut()", throwing = "throwable")
public void afterThrowing(Throwable throwable){
System.out.println("3-异常通知"+throwable);
}
@After(value = "pointCut()")
public void after(){
System.out.println("4-后置通知 ----- 无论程序是否出错都会执行这个通知");
}
@Around(value = "pointCut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
// 环绕通知中 这行代码用来判断方法是否调用。
// 如果有通知 则执行通知,如果没有通知则执行目标方法。
joinPoint.proceed();
}
}
第三步: 在applicationContext.xml配置文件中配置aop
<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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.atguigu"/>
<aop:aspectj-autoproxy proxy-target-class="false"/>
beans>
进行通过测试类 进行 测试
public class ProxyTest {
/**
* 测试静态代理
*/
@Test
public void testStaticProxy(){
StaticProxy.addUser();
}
/**
* 通过JDK动态代理 来实现动态代理
*/
@Test
public void testJDKProxy(){
UserService userService = new UserServiceImpl();
// 通过代理对象来调用目标方法。
UserService proxy = (UserService) MyJDKProxy.proxy(userService);
proxy.updateUser();
}
@Test
public void testCglibProxy(){
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) CglibProxy.proxy(userService);
proxy.addUser();
}
@Test
public void testSpringProxy(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getClass());
userService.addUser();
}
}
@Order(1)
注解来调整代理顺序。这个注解在类名上添加,数字越小表示级别越高。粒度
来进行配置
within("com.demo.*")
// demo包下的所有的类名不包括子孙包within("com.demo..*")
// demo包下的所有的类名包括子孙包exection( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
exection(* com.demo.*.add*(..))
* 用于通配符 com.demo.* 表示用于匹配所有的类 .add*() 表示所有以add开头的方法, (..) 表示该方法可以携带参数也可以不携带参数。