您的位置:首页 > 编程语言 > Java开发

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("将用户存入数据库");
}
}

完成!!


阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐