Spring之AOP注解失效原因和解决方法
2018-04-26 11:51
851 查看
问题:
在spring 中使用 @Transactional 、 @Cacheable 或 自定义 AOP 注解时,会发现个问题:在对象内部的方法中调用该对象的其他使用AOP注解的方法,被调用方法的AOP注解失效。事物失效public class UserService{ @Transactional public void hello(){ System.out.println("开始hello方法"); try { //在同一个类中的方法,再调用AOP注解(@Transactional注解也是AOP注解)的方法,会使AOP注解失效 //此时如果saveUser()存数据库动作失败抛出异常,“存入数据库“动作不会回滚,数据仍旧存入数据库 saveUser(); } catch (Exception e) { logger.error("发送消息异常"); } } @Transactional public void saveUser(){ User user = new User(); user.setName("zhangsan"); System.out.println("将用户存入数据库"); } }
2、缓存失效,或者自定义注解失效
//使用缓存,查询时先查询缓存,缓存中查询不到时,调用数据库。 @Cacheable(value = "User") public User getUser(String id){ System.out.println("查询数据库"); return UserDao.getUserById(id); } //在同一个类中的方法,调用@Cacheable注解的方法,会使AOP注解失效 public User getUser(String id){ //此时注解失效,getUser方法不会去缓存中查询数据,会直接查询数据库。 return getUser(id); }
原因:
java动态代理和 cglib 代理来创建AOP代理,没有接口的类使用cglib 代理。Spring AOP的java动态代理原理:
public interface PersonService { void hello(); } public class PersonServiceImpl implements PersonService { @Override public void hello() { System.out.println("你好我好大家好"); } }
//代理类实现InvovationHandler接口,来帮助被代理类去实现方法 public class HelloService implements InvocationHandler { private PersonService target; /** * 获取被代理对象 */ public Object getInstance(PersonService target) { this.target = target; Class clazz = target.getClass(); Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); return obj; } /** * 调用被代理对象的底层方法 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我是来打招呼的"); method.invoke(target, args); System.out.println("我已经打完招呼了"); return null; } public static void main(String[] args) { //获取被代理对象 PersonService personProxy = (PersonService) new HelloService().getInstance(new PersonServiceImpl()); //调用被代理对象的方法 personProxy.hello(); } }通过上面的描述,我们可以看出当方法被代理时,其实是动态生成了一个代理对象,代理对象去执行 invoke方法,在调用被代理对象的方法来完成业务。当在被代理对象的方法中调用被代理对象的方法时。其实是没有用代理调用,是通过被代理对象本身调用的。 在嘴上面的例子中,调用UserService中的hello()方法时,Spring的动态代理帮我们动态生成了一个代理的对象,暂且叫他$UserService。所以调用hello()方法实际上是代理对象$UserService调用的。但是在hello()方法内调用同一个类的另外一个注解方法saveUser()时,实际上是通过this.saveUser()执行的, this 指的是UserService 对象,并不是$UserService代理对象调用的,没有走代理。所以注解失效。
Spring解决方案
通过AopContext.currentProxy获取当前代理对象,通过代理对象调用方法。最好的方法是避免在方法内部调用。修改XML 新增如下语句;先开启cglib代理,开启 exposeProxy = true,暴露代理对象
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
public class UserService{ @Transactional public void hello(){ System.out.println("开始hello"); try { //通过代理对象去调用saveUser()方法 (UserService)AopContext.currentProxy().saveUser(); } catch (Exception e) { logger.warn("发送消息异常"); } } @Transactional public void saveUser(){ User user = new User(); user.setName("zhangsan"); System.out.println("将用户存入数据库"); } }
SpringBoot解决方案
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; }
- 通过实现ApplicationContext获取代理对象。新建获取代理对象的工具类SpringUtil
@Component public class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringUtil.applicationContext == null) { SpringUtil.applicationContext = applicationContext; } } //获取applicationContext public static ApplicationContext getApplicationContext() { return applicationContext; } //通过name获取 Bean. public static Object getBean(String name){ return getApplicationContext().getBean(name); } //通过class获取Bean. public static <T> T getBean(Class<T> clazz){ return getApplicationContext().getBean(clazz); } //通过name,以及Clazz返回指定的Bean public static <T> T getBean(String name,Class<T> clazz){ return getApplicationContext().getBean(name, clazz); }
public class UserService{ //买火车票 @Transactional public void hello(){ System.out.println("开始hello"); try { //通过代理对象去调用saveUser()方法 SpringUtil.getBean(this.getClass()).saveUser() } catch(Exception e) { logger.error("发送消息异常"); } } @Transactional public void saveUser(){ User user = new User(); user.setName("zhangsan"); System.out.println("将用户存入数据库"); } }
完成!!
相关文章推荐
- 【转】在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法
- 在同一个类中,一个方法调用另外一个有注解的方法,注解失效的原因和解决方法
- @Async,@Transational注解失效的原因和解决方法
- SpringBoot CGLIB AOP解决Spring事务,对象调用自己方法事务失效.
- spring 使用aop注解版会抛出奇怪的异常的解决方法
- 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法
- Spring配置文件使用@Value注解注入值的方法,以及AOP切面在Spring+SpringMVC中的不生效解决
- 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法
- 采用spring AOP 和注解解决日志
- Spring AOP不拦截从对象内部调用的方法原因
- Spring MVC @Transactional注解方式事务失效的解决办法
- Spring注解方式,异常 'sessionFactory' or 'hibernateTemplate' is required的解决方法 转载的,劳记劳记
- spring 注解aop切入点表达式怎么排除某些方法
- spring aop之对象内部方法间的嵌套失效
- PHP Header失效的原因分析及解决方法
- spring注解实现业务层事务管理,当业务层自调用时,事务失效问题解决
- 让spring3.0可以使用aop注解的方法是导入两个jar包aspectjrt.jar aspectjweaver.jar
- 从头认识Spring-3.6 简单的AOP日志实现(注解版)-需要记录方法的运行时间
- spring事务失效解决方法并进行事务的部分回滚
- Spring 注解 AOP 出现错误::0 can't find referenced pointcut XXX 的原因