源代码下载链接地址:
https://download.csdn.net/download/weixin_46411355/87549575历史原因: JDK动态代理要求必须"有接口",但是某些类它没有接口,则无法使用JDK代理生成代理对象. 所以为了填补知识的空缺,则引入cglib代理.
问题说明: cglib动态代理 要求有无接口都可以创建代理对象. 问题? 如何保证和被代理者"相同"
答案(特点): 要求cglib动态代理继承被代理者.代理对象是被代理者的子类.
代理类和被代理类(目标类)两者是父子关系,代理对象是目标类的子类。
说明1: 一般我们将业务层中的耦合性高的代码,采用动态代理的方式进行解耦.使得程序更加具有扩展性. (业务逻辑的解耦)
说明2: Spring专门针对动态代理的规则.封装了一套API 起名 AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.
1).连接点: 用户可以被扩展的方法
2).切入点: 用户实际扩展的方法
3).通知: 扩展方法的具体实现
4).切面: 将通知应用到切入点的过程
org.springframework.boot spring-boot-starter-aop
SpringConfig.java
package com.jt.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@ComponentScan("com.jt")
@Configuration
public class SpringConfig {}
package com.jt.service;public interface UserService {void addUser();void deleteUser();
}
package com.jt.service;import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Overridepublic void addUser() {System.out.println("完成用户新增");}@Overridepublic void deleteUser() {System.out.println("完成用户删除操作");}
}
切入点表达式
package com.jt;import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring_AOP {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = applicationContext.getBean(UserService.class);//如果是实现类对象,则方法没有被扩展//如果是代理对象,则方法被扩展 aop有效的System.out.println(userService.getClass());//class com.jt.service.UserServiceImpl$$EnhancerBySpringCGLIB$$baeada27userService.addUser();}
}
说明: 编辑配置类,添加@EnableAspectJAutoProxy,让AOP机制有效
package com.jt.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@ComponentScan("com.jt")
@Configuration
@EnableAspectJAutoProxy//让spring中的AOP生效
public class SpringConfig {}
package com.jt;import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring_AOP {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);//理论值:根据接口获取实现类对象 但是与切入点表达式匹配,为了后续扩展方便,为其创建代理对象UserService userService = applicationContext.getBean(UserService.class);//如果是实现类对象,则方法没有被扩展//如果是代理对象,则方法被扩展 aop有效的(是代理对象)/*getClass()是Object中的方法,不拦截*/System.out.println(userService.getClass());//class com.jt.service.UserServiceImpl$$EnhancerBySpringCGLIB$$baeada27userService.addUser();}
}
说明: AOP是一种抽象的一种概念,看不见/摸不着.所以需要大家对概念有自己的认知.
@Pointcut(“bean(userServiceImpl)”) 只匹配ID为userServiceImpl的对象
@Pointcut(“within(com.jt.service.*)”) 匹配xx.xx.service下的所有对象
@Pointcut("execution(* com.jt.service..*.*(..))")
拦截返回值类型任意 xx.xx.service包下所有子孙包的所有类的任意方法
@Pointcut("execution(* com.jt.service..*.add*(..))")
拦截返回值类型任意 xx.xx.service包下所有子孙包的所有类.以add开头的方法
package com.jt.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD) //注解对方法有效
@Retention(RetentionPolicy.RUNTIME) //运行期有效
public @interface MyAnnotation {//注解起标记作用}
@Pointcut("@annotation(com.jt.anno.MyAnnotation)")public void pointCutMethod(){}
测试类运行
package com.jt.anno;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Find {int id() default 0;
}
在service层的实现类UserServiceImp的addUser()方法上面添加自定义注解@Find(id=101)
利用前置通知,打印注解中的id值!!!
/*** 知识点:* 如果切入点表达式只对当前通知有效,则可以按照如下方式编辑* 要求:动态的拦截Find注解,并且要获取Find注解中的参数Id* 难点:动态获取注解的对象!!* 代码解释:* 1.@annoattion(find) 拦截find变量名称对应类型的注解* 2.当匹配该注解后,将注解对象当做参数传递给find* 优势:可以一步到位获取注解的内容,避免了反射的代码*/@Before("@annotation(find)")public void before2(Find find){System.out.println("ID的值为:"+find.id());}
1.前置通知 在目标方法执行之前执行.
2.后置通知 在目标方法执行之后执行.
3.异常通知 在目标方法执行之后抛出异常时执行.
4.最终通知 都要执行的通知
说明: 上述的四大通知一般用于记录程序的运行状态.只做记录.
5.环绕通知 在目标方法执行前后都要执行的通知
切面类
SpringAOP.java
/*** 定义通知方法:* 1.前置通知 在目标方法执行之前执行* 2.后置通知 在目标方法执行之后执行* 3.异常通知 在目标方法执行之后抛出异常时执行* 4.最终通知 都要执行的通知* 5.环绕通知 在目标方法执行前后都要执行的通知**记录程序的状态* 1.目标对象的class/类路径 com.jt.xx.xxx.UserServiceImpl* 2.目标对象的方法名* 3.目标对象的方法的参数信息* 4.获取目标对象方法的返回值* 5.获取目标对象执行报错的异常信息*/@Before("pointCutMethod()")public void before(JoinPoint joinPoint){//1.获取目标对象的类型Class> targetClass = joinPoint.getTarget().getClass();//2.获取目标对象的路径String path = joinPoint.getSignature().getDeclaringTypeName();//3.获取目标对象的方法名称String methodName = joinPoint.getSignature().getName();//4.获取方法的参数Object[] args = joinPoint.getArgs();System.out.println("类型" + targetClass);System.out.println("类的路径:" + path);System.out.println("方法名:" + methodName);System.out.println("参数:" + Arrays.toString(args));}
运行结果
package com.jt.service;public interface UserService {void addUser();void deleteUser();int findCount();//查询总数
}
package com.jt.service;import com.jt.anno.Find;
import com.jt.anno.MyAnnotation;
import org.springframework.stereotype.Service;@Service
public class UserServiceImpl implements UserService {@Find(id = 101)@MyAnnotation//标记作用@Overridepublic void addUser() {System.out.println("完成用户新增");}@Overridepublic void deleteUser() {System.out.println("完成用户删除操作");}/*** 测试获取返回值的!!!* @return*/@MyAnnotation@Overridepublic int findCount() {return 1000;}
}
//注意事项:如果多个参数,joinPoint必须位于第一位!!!@AfterReturning(value="pointCutMethod()",returning = "result")public void afterReturn(JoinPoint joinPoint,Object result){//如果需要获取当前的方法信息,则可以通过joinPoint获取
// System.out.println("我是后置通知");System.out.println("我是后置通知,获取方法的返回值"+result);}
package com.jt;import com.jt.config.SpringConfig;
import com.jt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class TestSpring_AOP02 {public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);//理论值:根据接口获取实现类对象 但是与切入点表达式匹配,为了后续扩展方便,为其创建代理对象UserService userService = applicationContext.getBean(UserService.class);//如果是实现类对象,则方法没有被扩展//如果是代理对象,则方法被扩展 aop有效的(是代理对象)/*getClass()是Object中的方法,不拦截*/System.out.println(userService.getClass());//class com.sun.proxy.$Proxy19userService.addUser();userService.findCount();//测试代返回值的方法}
}
throwing:获取异常信息,之后进行传递
//后置通知与异常通知是互斥的,只能有一个@AfterThrowing(value = "pointCutMethod()",throwing = "exception")public void afterThrow(JoinPoint joinPoint,Exception exception){//打印异常//exception.printStackTrace();System.out.println("我是异常通知:"+exception.getMessage());}
@Configuration 标识当前类是配置类
@ComponentScan 包扫描注解 扫描注解
@Bean 标识该方法的返回值交给Spring容器管理
@Scope 控制多例和单例
@Lazy 懒加载
@PostConstruct 初始化方法
@PreDestroy 销毁方法
@Component 将当前类未来的对象交给容器管理
@Autowired 按照类型进行注入
@Qualifier 按照名称进行注入
@Repository 标识持久层注解
@Service 标识Service层
@Controller 标识Controller层
@Value 为属性赋值 @Value(“${key}”)
@PropertySource 加载指定路径的配置文件properties
@Aspect 标识当前类是一个切面类
@Pointcut 用于定义切入点表达式 表达式写法4种
@EnableAspectJAutoProxy 让AOP的注解有效果
@Before AOP-前置通知
@AfterReturning AOP-后置通知
@AfterThrowing AOP-异常通知
@After AOP-最终通知
@Around AOP-环绕通知