当前位置: 代码迷 >> 综合 >> SpringAOP-静态、动态代理
  详细解决方案

SpringAOP-静态、动态代理

热度:84   发布时间:2024-02-22 20:01:11.0

SpringAOP

实现代码复用,保持原有代码的结构(流程)不被改变,增强功能。

静态代理

在程序运行前就以经存在代理类的字节码文件,代理对像和被代理对象在运行前已经被确定。

优点:1、业务类只需关注业务类本身,保证了业务类的重用性。

? 2、把真实对象隐藏起来,保护真实对象。

缺点:1、代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建-个代理对象。
2、如果需要代理的方法很多,则要为每一种方法都进行代理处理。
3、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

自己理解:新建一个类,被代理类作为一个成员变量,在创建代理类时,传入一个被代理类为代理类的成员变量赋值,在代理类中重写被代理类中需要代理的方法,在方法中用成员变量调用被代理类的方法,参入参数,获取返回值,在执行被代理类的方法的前后增加功能,作到不不改变业务逻辑的情况下,为系统增加功能。

例:

被代理类:

public class AdminServiceImpl implements AdminService{
    @Overridepublic Admin login(String name, String pwd) {
    if ("121".equals(name) && "212".equals(pwd)){
    return new Admin("121","212");}return null;}@Overridepublic Admin add(String name, String pwd) {
    return new Admin("121","212");}
}

代理类:

public class AdminProxy {
    private AdminService service;public AdminProxy(AdminService service){
    this.service=service;}public Admin login(String name, String pwd) {
    Admin login = service.login(name, pwd);if (login!=null){
    System.out.println("登录成功");}return login;}
}

测试:

@Test
public void oneTest(){
    AdminService service = new AdminServiceImpl();AdminProxy proxy = new AdminProxy(service);Admin login = proxy.login("121", "212");System.out.println(login);
}

动态代理

在程序运行期间由jvm通过反射等机制动态的创建出代理对象的字节码,代理对象和真实对象的关系是在程序运行时才确定的。

jdk动态代理

Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象

被代理类必须有一个实现de接口。

自己理解:运用Proxy.newProxyInstance(1,2,3)方法传入代理类的1ClassLoader对象,2interfaces数组和3一个实现InvocationHandler接口的自定义对象,在自定义对象中重写invoke方法,使用method.getName()过去当前调用被代理类对象的方法名,执行相应的增强操作。

例:

public class ProxyTest2 {
    //被代理类AdminService service = new AdminServiceImpl();@Testpublic void Test2(){
    //获取AdminService的ClassLoader对象ClassLoader classLoader = service.getClass().getClassLoader();//获取Class<?>[] interfacesClass<?>[] interfaces = new Class[]{
    AdminService.class};//代理类AdminService adminService = (AdminService)Proxy.newProxyInstance(classLoader, interfaces, new MyAdminProxy2());Admin login = adminService.login("121", "212");System.out.println(login);adminService.add("21","21");}class MyAdminProxy2 implements InvocationHandler{
    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println(method.getName());Object invoke = method.invoke(service, args);return invoke;}}
}

cglib动态代理

生成当前被代理类的子类

原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对fina修饰的类进行代理。
1、CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2、要求类不能是final的,要拦截的方法要是非final、 非static、 非private的。
3、动态代理的最小单位是类(所有类中的方法都会被处理);

自己理解:为一个Enhancer对象设置superClass即被代理类,和实现MethodInterceptor的自定义回调函数相关联,重写intercept方法,执行增强操作(获取当前方法,执行对应增强操作)。调用Enhancer的creat函数创建关联对象,强转为被代理类对象,即可调用被代理类对象的方法。在执行方法时,会执行相对应的增强操作。传入参数和返回值都不会改变。

例:

public class EnhancerTest {
    @Testpublic void test1(){
    //生成Enhancer对象Enhancer enhancer = new Enhancer();//指定Enhancer的字节码对象enhancer.setSuperclass(AdminServiceImpl.class);//设置回调函数enhancer.setCallback(new MyMethodInterceptor());AdminService service = (AdminService)enhancer.create();Admin login = service.login("121", "212");System.out.println(login);}class MyMethodInterceptor implements MethodInterceptor {
    @Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    Object invoke = methodProxy.invokeSuper(o, objects);if ("login".equals(method.getName())){
    System.out.println("login");}return invoke;}}
}

AOP术语

连接点:一般指方法

切入点:需要增强功能的方法集合,确定通知的执行点

通知:真实用于增强方法的操作(功能)

切面:切入点+通知

通知

执行顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5KUz0ls-1601045200168)(C:\Users\84442\AppData\Roaming\Typora\typora-user-images\1595509349378.png)]

基于xml的aop

被代理类:

public class User {
    public boolean login(String id){
    if ("11".equals(id)){
    System.out.println("用户存在");//return true;throw new RuntimeException("test");}return false;}
}

代理类:

public class demo1 {
    //前置通知public void before(){
    System.out.println("有人查询用户是否存在");}//后置通知public void after(){
    System.out.println("after");}//完成通知public void afterRun(){
    System.out.println("afterRun");}//异常通知public void afterThrowing(){
    System.out.println("afterThrowing");}//环绕通知public void around(ProceedingJoinPoint point){
    try {
    point.proceed();} catch (Throwable throwable) {
    throwable.printStackTrace();}}
}

xml配置:

<bean class="com.offcn.User" id="user"/><bean class="com.offcn.demo1" id="demo1"/><aop:config><aop:pointcut id="pointcut" expression="execution(* com.offcn.User.*(..))"/><aop:aspect ref="demo1"><!--前置通知--><aop:before method="before" pointcut-ref="pointcut"/><!--后置通知--><aop:after method="after" pointcut-ref="pointcut"/><!--完成通知--><aop:after-returning method="afterRun" pointcut-ref="pointcut"/><!--异常通知--><aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/></aop:aspect>
</aop:config>

基于注解的aop

编辑切面类(通知所在类):

@Aspect
@Component
public class demo2 {
    //前置通知public void before(){
    System.out.println("有人查询用户是否存在");}//后置通知public void after(){
    System.out.println("after");}//完成通知public void afterRun(){
    System.out.println("afterRun");}//异常通知public void afterThrowing(){
    System.out.println("afterThrowing");}//环绕通知public void around(ProceedingJoinPoint point){
    try {
    point.proceed();} catch (Throwable throwable) {
    throwable.printStackTrace();}}
}

必须注解@Aspect声明当前类是切面类

在spring中配置AOP代理自动配置

开启AOP代理自动配置

xml方式

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

注解方式

在配置类上添加

@EnableAspectJAutoProxy注解

全局与局部切入点

局部切入点

切入点方法:在切面类中定义方法,注解@Before、@After、@AfterReturning、@AfterThrowing、@Around

例:

无参局部

//前置通知
@Before("execution(* com.offcn.User.*(..))")
public void before(){
    System.out.println("有人查询用户是否存在");
}

有参局部

//后置通知
@After("execution(* com.offcn.User.*(..)) && args(id)")
public void after(String id){
    System.out.println(id);System.out.println("after");
}

全局切入点

在切面类中声明一个返回值为void的空方法,注解Pointcut(“aspect表达式 && 参数列表”)

例:

 @Pointcut("execution(* com.offcn.spring.service.*.*(..)) && args(userName,userAge,birth)")public void beforePointCut(){
    }

调用:

@Before("beforePointCut() && args(userName,userAge,birth)")
public void check(String userName, int userAge, Date birth) {
    System.out.println("权限校验......" + userName + " " + userAge + " " + birth);
}

作用:使check方法成为beforePointCut()的aspect表达式匹配的内容的before通知,传入参数。

注:args中的参数名要和check的参数列表的参数名相同

? beforePointCut()方法可以在其他的饿切面类中调用

有参环绕通知:

@Around("execution(* com.offcn.User.*(..)) && args(id)")
public boolean around(ProceedingJoinPoint point,String id){
    try {
    boolean proceed = (boolean) point.proceed();System.out.println(proceed);return proceed;} catch (Throwable throwable) {
    throwable.printStackTrace();}return false;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPi7gXza-1601045200193)(C:\Users\84442\AppData\Roaming\Typora\typora-user-images\1595572790771.png)]