spring 纯注解方式 与AOP
spring注解方式
以前我也使用过纯注解方式.现在在这里做个记录
我们先认识几个我们都耳熟能详的注解
- @configuration :从spring3.0这个注解就可以用于定义配置类,可以替换xml配置文件,相当于beans的根标签,配置类中可以包含一个或者多个@bean注解这些方法都会被一个
AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
- @bean: 相当于 bean
- @componentScan
- @PropertySource 相当于context:property-placeholder标签 如果是在类路径下,需要加上classpath 和@value 配合使用
- /**
- * 相当于一个beans
- */
- @Configuration
- @ComponentScan(basePackages = "xiaodao.spring")
- public class SpringConfiguration {
- public SpringConfiguration() {
- System.out.println("spring 容器启动");
- }
- /**
- * bean id 默认是bean的方法名
- * @return
- */
- /* @Bean
- public UserService userService(){
- return new UserServiceImpl();
- }*/
- }
- public class SpringConfigurationTest {
- @Test
- public void test1(){
- //创建纯注解方式的spring 容器
- ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
- UserService userService = (UserService) applicationContext.getBean("userService");
- userService.save();
- }
- }
spring 分模块开发
@import 注解的使用方法
我们在spring中有各种各样的配置文件.写在一个类中,肯定是 不合适的.我们需要分门分类
- @Configuration
- @ComponentScan(basePackages = "com.kkb.spring")
- @Import({ JdbcConfig.class})
- public class SpringConfiguration {
- }
- @Configuration
- @PropertySource("classpath:jdbc.properties")
- public class JdbcConfig{
- }
什么是AOP呢?
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程
AOP最早是AOP联盟的组织提出的,指定的一套规范,spring将AOP的思想引入框架之中,通过预编译方式和运行期间动态代理实现程序的统一维护的一种技术,
预编译方式在spring中没有使用,因为要结合其他编译方式,和spring造成强耦合,预编译方式就是在程序没有运行前编译.
AOP是OOP的延续,,AOP解决的是从横向解决代码重复的问题 ,OOP是从纵向解决代码的重复问题
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存)讲业务逻辑和系统处理的代码进行解耦 如如:关闭连接 事物管理,操作日志的记录
AOP 的相关术语?
1. Joinpoint(连接点) -- 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
2. Pointcut(切入点) -- 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
3. Advice(通知/增强) -- 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
4. Introduction(引介) -- 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
5. Target(目标对象) -- 代理的目标对象
6. Weaving(织入) -- 是指把增强应用到目标对象来创建新的代理对象的过程
7.Proxy(代理) -- 一个类被AOP织入增强后,就产生一个结果代理类
8. Aspect(切面) -- 是切入点和通知的结合,以后咱们自己来编写和配置的
AOP实现之AspectJ(了解)
***AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)
***可以这样说AspectJ是目前实现AOP框架中最成熟,功能最丰富的语言,更幸运的是,AspectJ与java程序完全兼容,几乎是无缝关联,因此对于有java编程基础的工程师,上手和使用都非常容易。
***了解AspectJ应用到java代码的过程(这个过程称为织入),对于织入这个概念,可以简单理解为aspect(切面)应用到目标函数(类)的过程。
***对于这个过程,一般分为动态织入和静态织入,动态织入的方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成的,如Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),Spring AOP采用的就是基于运行时增强的代理技术
***ApectJ采用的就是静态织入的方式。ApectJ主要采用的是编译期织入,在这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在java目标类编译时织入,即先编译aspect类再编译目标类。
Spring AOP实现原理分析
spring AOP 是通过动态代理技术实现的
而动态代理的技术是通过反射来实现的
动态代理技术的实现方式有两种:基于接口的JDK动态代理和基于继承的CGLib动态代理。
我们来写一下 jdk的代理和cglib的代理类
我们要代理的对象
- public interface UserService {
- public void save();
- }
- @Service("userService")
- public class UserServiceImpl implements UserService {
- public void save() {
- System.out.println("userserviceimpl save方法");
- }
- }
代理的实现
- public class MyProxyUtils {
- /**
- * 使用JDK动态代理类
- * @param userServiceInterface
- * @return
- */
- public static UserService getProxy(final UserService userServiceInterface){
- /**
- * proxy是jdk中的代理类
- * 1.目标对象的类加载器
- * 2.目标对接的接口
- * 3.代理对象的执行处理器
- */
- UserService userService = (UserService) Proxy.newProxyInstance(userServiceInterface.getClass().getClassLoader(), userServiceInterface.getClass().getInterfaces(), new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("记录日志-开始");
- //下面的代码是,反射中的api用法
- //方法
- //2.参数
- //这行代码实际还是调用目标对象的方法
- Object invoke = method.invoke(userServiceInterface, args);
- System.out.println("记录日志结束");
- return invoke;
- }
- });
- return userService;
- }
- /**
- * 使用cglib的方式动态代理技术实现
- * 它是基于继承的方式实现的
- * @param userService
- * @return
- */
- public static UserService getProxyByCglib(UserService userService){
- //创建增强器
- Enhancer enhancer =new Enhancer();
- //这里设置增强类的类对象-实现类的类对象
- enhancer.setSuperclass(UserServiceImpl.class);
- //设置回调函数
- enhancer.setCallback(new MethodInterceptor() {
- /**
- *
- * @param o
- * @param method
- * @param args 方法参数
- * @param methodProxy 代理之后的对象的方法
- * @return
- * @throws Throwable
- */
- public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
- long start = System.currentTimeMillis();
- System.out.println("cglib 记录开始时间: "+start);
- //代理对象是目标的对象的子类
- //这行代码实际还是调用目标对象的方法
- //o 是代理对象
- Object object = methodProxy.invokeSuper(o, args);
- long end = System.currentTimeMillis();
- System.out.println("cglib 记录结束时间: "+end);
- return object;
- }
- });
- // 获取增强之后的代理对象.
- return (UserService) enhancer.create();
- }
- }
我们的测试类
- public class MyProxyUtilsTest {
- @Test
- public void testjdkProxy(){
- UserService userService = new UserServiceImpl();
- UserService proxy = MyProxyUtils.getProxy(userService);
- userService.save();
- System.out.println("===========");
- proxy.save();
- }
- @Test
- public void testCglibProcy(){
- UserService userService = new UserServiceImpl();
- UserService proxy = MyProxyUtils.getProxyByCglib(userService);
- userService.save();
- System.out.println("===========");
- proxy.save();
- }
- }
- userserviceimpl save方法
- ===========
- cglib 记录开始时间: 1556369092690
- userserviceimpl save方法
- cglib 记录结束时间: 1556369092700
切入点表达式
execution:必须要([修饰符] 返回值类型 包名.类名.方法名(参数))
- 修饰符可以省略掉
- 返回值类型必须要 可以用*来代替
- 包名:多级包之间使用.分割 包名可以用*号代替,省略中间的包名可以用..
- 类名可以用*代替,也可以写成*DaoImpl
- 方法名可以用*代替 *add
- 参数可以用*代替 如果有多个参数可以..代替
xml 实现aop
aop总共有5中通知
前置通知
后置通知
异常通知
后置通知
环绕通知
xml 配置文件
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd">
- <bean class="xiaodao.spring.service.UserServiceImpl"></bean>
- <!--增强类-->
- <bean id="myAdvice" class="xiaodao.spring.MyAdvice"></bean>
- <!--aop 配置-->
- <aop:config>
- <!--配置aop切面,切面是由通知和切入点组成的-->
- <aop:aspect ref="myAdvice">
- <!--指定切入点.需要通过表达式来指定 method增强类的方法-->
- <!--<aop:before method="log" pointcut="execution(void xiaodao.spring.service.UserServiceImpl.save())"/>-->
- <!--<aop:after method="log2" pointcut="execution(void xiaodao.spring.service.UserServiceImpl.save())"/>-->
- <!--<aop:after-returning method="log3" pointcut="execution(* xiaodao.spring.*.UserServiceImpl.save())"/>-->
- <!--<!–异常通知 –>-->
- <!--<aop:after-throwing method="log4" pointcut="execution(* xiaodao.spring.*.UserServiceImpl.save())"/>-->
- <aop:around method="log5" pointcut="execution(* xiaodao.spring.*.UserServiceImpl.save())"/>
- </aop:aspect>
- </aop:config>
- </beans>
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("classpath:application.xml")
- public class TestAOP {
- @Autowired
- private UserService userService;
- @Test
- public void test(){
- userService.save();
- }
- }
增强类:
- public class MyAdvice {
- public void log(){
- System.out.println("记录日志.....");
- }
- public void log2(){
- System.out.println("记录日志 后置通知.....");
- }
- public void log3(){
- System.out.println("记录日志..最终通知..");
- }
- public void log4(){
- System.out.println("记录日志..异常...");
- }
- public void log5(ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("环绕通知前");
- //调用目标对象
- try {
- joinPoint.proceed();
- System.out.println("后置通知");
- } catch (Throwable throwable) {
- System.out.println("环境通知..异常");
- throwable.printStackTrace();
- }finally {
- System.out.println("最终通知");
- }
- }
- }
- userservice 还是刚刚那个userservice
@Service("userService")- public class UserServiceImpl implements UserService {
- public void save() {
- System.out.println("userserviceimpl save方法");
- System.out.println(1/0);
- }
- }
注解实现AOP
- @Aspect
- @Component("myaspect")
- //相当于<aop:aspectj-proxy>
- @EnableAspectJAutoProxy
- public class MyAspect {
- //定义该方法是一个前置通知
- @Before(value = "execution(* xiaodao.spring.service.*.*())")
- public void before(){
- System.out.println("注解前置通知");
- }
- @After(value = "execution(* xiaodao.spring.service.*.*())")
- public void after(){
- System.out.println("注解后置通知");
- }
- @AfterReturning(value = "func()")
- public void end(){
- System.out.println("最终通知");
- }
- @Pointcut(value = "execution(* xiaodao.spring.service.*.*())")
- public void func(){
- }
- }
本来准备全注解实现,目前还不可以junit还是需要加载application.xml 配置文件,暂时不知道junit如何使用我自定义的注解配置类
Spring AOP 采用哪种代理?
JDK 动态代理和 CGLIB 动态代理均是实现 Spring AOP 的基础。对于这一块内容,面试官问的比较多,他们往往更想听听面试者是怎么回答的,有没有看过这一块的源码等等。
针对于这一块内容,我们看一下 Spring 5 中对应的源码是怎么说的。
- public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
- @Override
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class<?> targetClass = config.getTargetClass();
- if (targetClass == null) {
- throw new AopConfigException("TargetSource cannot determine target class: " +
- "Either an interface or a target is required for proxy creation.");
- }
- // 判断目标类是否是接口或者目标类是否Proxy类型,若是则使用JDK动态代理
- if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
- return new JdkDynamicAopProxy(config);
- }
- // 配置了使用CGLIB进行动态代理或者目标类没有接口,那么使用CGLIB的方式创建代理对象
- return new ObjenesisCglibAopProxy(config);
- }
- else {
- // 上面的三个方法没有一个为true,那使用JDK的提供的代理方式生成代理对象
- return new JdkDynamicAopProxy(config);
- }
- }
- //其他方法略……
- }
从上述源码片段可以看出,是否使用 CGLIB 是在代码中进行判断的,判断条件是 config.isOptimize()、config.isProxyTargetClass() 和 hasNoUserSuppliedProxyInterfaces(config)。
其中,config.isOptimize() 与 config.isProxyTargetClass() 默认返回都是 false,这种情况下判断结果就由 hasNoUserSuppliedProxyInterfaces(config) 的结果决定了。
- public class ProxyConfig implements Serializable {
- /** use serialVersionUID from Spring 1.2 for interoperability */
- private static final long serialVersionUID = -8409359707199703185L;
- private boolean proxyTargetClass = false;
- private boolean optimize = false;
- boolean opaque = false;
- boolean exposeProxy = false;
- private boolean frozen = false;
- .........
- }
简单来说,hasNoUserSuppliedProxyInterfaces(config) 就是在判断代理的对象是否有实现接口,有实现接口的话直接走 JDK 分支,即使用 JDK 的动态代理。
所以基本上可以总结出 Spring AOP 中的代理使用逻辑了:如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;如果目标对象没有实现了接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换。
spring 纯注解方式 与AOP的更多相关文章
- (转)使用Spring的注解方式实现AOP的细节
http://blog.csdn.net/yerenyuan_pku/article/details/52879669 前面我们已经入门使用Spring的注解方式实现AOP了,现在我们再来学习使用Sp ...
- (转)使用Spring的注解方式实现AOP入门
http://blog.csdn.net/yerenyuan_pku/article/details/52865330 首先在Eclipse中新建一个普通的Java Project,名称为spring ...
- Spring的注解方式实现AOP
Spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,asp ...
- 使用Spring的注解方式实现AOP
Spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,asp ...
- Spring 使用纯注解方式完成IoC
目录 创建一个简单的Person类 使用xml方式配置Spring容器并获取bean的过程 创建xml配置文件 进行测试 使用纯注解方式配置Spring容器并获取bean的过程 创建spring配置类 ...
- Spring系列之aAOP AOP是什么?+xml方式实现aop+注解方式实现aop
Spring系列之aop aop是什么?+xml方式实现aop+注解方式实现aop 什么是AOP? AOP为Aspect Oriented Programming 的缩写,意识为面向切面的编程,是通过 ...
- mybatis源码学习--spring+mybatis注解方式为什么mybatis的dao接口不需要实现类
相信大家在刚开始学习mybatis注解方式,或者spring+mybatis注解方式的时候,一定会有一个疑问,为什么mybatis的dao接口只需要一个接口,不需要实现类,就可以正常使用,笔者最开始的 ...
- 基于AspectJ的注解方式进行AOP开发
-------------------siwuxie095 基于 AspectJ 的注解方式进行 AOP 开发 ...
- 使用注解方式实现 AOP和IoC
使用注解方式实现AOP和IoC IOC和DI的注解 IOC: @Component:实现Bean组件的定义 @Repository:用于标注DAO类,功能与@Component作用相当 @Servic ...
随机推荐
- nginx反向代理配置
最近在项目中使用nginx反向代理,根据不同的请求路径,将请求分发到不同服务.下面的示例主要完成如下功能 /prod/路径的请求分发到prod服务 /test/路径的请求分发到test服务 创建文件夹 ...
- mysql中截取指定字符前后的字符串
使用SUBSTRING_INDEX()函数substring_index(str,delim,count) str:要处理的字符串 delim:分隔符 count:分隔符计数 例子取出上述表中数组的第 ...
- GitHub的Repository权限将public转为private
2019年1月7日,GitHub CEO Nat Friedman 于官方博客公开发文,称“New year, new GitHub”,宣布从此将免费无限地为普通用户提供私有仓库服务. 因此,我们可以 ...
- Mybatis之旅第二篇-Mapper动态代理方式
一.引言 通过上一篇mybatis的入门学习,我们已经会使用mybatis实现简单的增删改查,但是我们也发现了用原始Dao开发的一些问题: Dao方法体存在重复代码:通过SqlSessionFacto ...
- 前端神器-神级代码编辑软件Sublime Text下载、使用教程、插件推荐说明、全套快捷键
Sublime Text 是一个代码编辑器,也是HTML和散文先进的文本编辑器.Sublime Text是由程序员Jon Skinner于2008年1月份所开发出来,它最初被设计为一个具有丰富扩展功能 ...
- 进阶!基于CentOS7系统使用cobbler实现单台服务器批量自动化安装不同版本系统(week3_day5_part2)-技术流ken
前言 在上一篇博文<cobbler批量安装系统使用详解-技术流ken>中已经详细讲解了cobbler的使用以及安装,本篇博文将会使用单台cobbler实现自动化批量安装不同版本的操作系统. ...
- Asp.Net MVC Https设置
1. IIS设置 1.1 创建SSL证书 点击左侧菜单栏顶部,点击“功能视图”里的“服务器证书”: 点击“创建自动签名证书”创建自动签名证书: 1.2 设置SSL证书 点开网站,在“功能视图”里点 ...
- Eureka的工作原理以及它与ZooKeeper的区别
1.Eureka 简介: Eureka 是 Netflix 出品的用于实现服务注册和发现的工具. Spring Cloud 集成了 Eureka,并提供了开箱即用的支持.其中, Eureka 又可细分 ...
- ubuntu 安装vm-tool
1.“虚拟机”->“安装vmware tools”VMware tools 2. 新建一个文件夹 ,打开vmware tools安装介质.右键选择vmwaretools的gz压缩包,选择“提取到 ...
- IntelliJ IDEA下的使用git
1.git简介 git是目前流行的分布式版本管理系统.它拥有两套版本库,本地库和远程库,在不进行合并和删除之类的操作时这两套版本库互不影响.也因此其近乎所有的操作都是本地执行,所以在断网的情况下任然可 ...