栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring 深入学习 IoC/AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring 深入学习 IoC/AOP

Spring 深入学习

文章目录
  • Spring 深入学习
    • 一、本章目标
    • 二、Spring 增强
        • 2.1.异常抛出增强
        • 2.2.最终增强
        • 2.3.环绕增强
    • 三、依赖注入
        • 3.1.构造注入
        • 3.2.Set 注入 (重点)
        • 3.3扩展的注入
          • 3.3.1.常量注入
          • 3.3.2Bean注入
          • 3.3.3数组注入
          • 3.3.4List注入
          • 3.3.5Map注入
          • 3.3.6set注入
          • 3.3.7Null注入
          • 3.3.8Properties 注入
          • 3.3.9P命名和C命名注入
    • 四、使用注解实现Spring IoC
        • 4.1.使用注解实现Spring IoC
        • 4.2.使用注解实现Spring IoC
        • 4.3.使用注解实现Spring IoC
    • 五、使用Java标准注解完成装配
        • 5.1.使用Java标准注解完成装配2-1
        • 5.2.使用Java标准注解完成装配2-2
    • 六、使用注解实现Spring AOP
        • 6.1.使用注解实现Spring AOP
        • 6.2.使用注解实现Spring AOP
    • 七、使用注解定义其他类型的增强
        • 7.1.使用注解定义异常抛出增强
        • 7.2.使用注解定义最终增强
        • 7.3.使用注解定义环绕增强
        • 7.4.注解使用小结
    • 八、本章总结

一、本章目标
  1. 掌握异常抛出增强、最终增强、环绕增强的使用方法
  2. 理解构造注入
  3. 掌握p命名空间注入
  4. 掌握不同数据类型的注入方法
  5. 掌握使用注解的方式实现IoC
  6. 掌握使用注解的方式实现AOP
二、Spring 增强 2.1.异常抛出增强

特点

  1. 在目标对象方法抛出异常时织入增强处理
  2. 可灵活拔插的异常处理方案

语法:


	 
		
	 

实现步骤:

1.在maven中添加依赖包



   org.aspectj
   aspectjweaver
   1.9.6

2.定义用于增强方法的JavaBean

public class ErrorLogger {
	private static final Logger log = Logger.getLogger(ErrorLogger.class);
	public void afterThrowing(JoinPoint jp, RuntimeException e) {
		log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
	}
}

3.配置切面





     
     
     
     
         
         
         
     

4.在dao实现中模拟一个小错误

public class UserDaoImpl implements UserDao {
 //模拟操作数据库
 public void saveUser(User user) { 
     System.out.println("保存用户信息到数据库");
     throw new RuntimeException("为测试程序运行效果抛出的异常");
 }
}

5.测试

@org.junit.Test
public void testAfterThrowing() {
  ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  UserService service = ctx.getBean("service", UserService.class);
  User user = new User();
  user.setId(1);
  user.setUsername("aiden");
  user.setPassword("666666");
  user.setEmail("aiden@163.com");
  service.save(user);
}

6.运行结果


2.2.最终增强

特点

  • 无论方法正常运行还是抛出异常,都会在目标方法最后织入增强处理,即:该增强都会得到执行
  • Java中finally代码块的作用相似,通常用于释放资源
  • 可灵活拔插

语法:


	
		
	

实现步骤

1.引入依赖包

2.定义增强的Javabean

public class AfterLogger {
	private static final Logger log = Logger.getLogger(AfterLogger.class);
	//最终增强方法
	public void afterLogger(JoinPoint jp) {
		log.info(jp.getSignature().getName() + " 方法结束执行。");
	}
}

3.配置切面





     
     
     
         
         
     

4.测试

@org.junit.Test
public void testAfterLogger() {
  ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  UserService service = ctx.getBean("service", UserService.class);
  User user = new User();
  user.setId(1);
  user.setUsername("aiden");
  user.setPassword("666666");
  user.setEmail("aiden@163.com");
  service.save(user);
}

5.运行结果


2.3.环绕增强

特点
目标方法前后都可织入增强处理,可获取或修改目标方法的参数、返回值
可对目标方法进行异常处理,甚至可以决定目标方法是否执行


	
		
	

实现步骤

1.引入依赖包

​ 同上。

2.定义增强的Javabean

 
public class AroundLogger {
    private static final Logger log = Logger.getLogger(AroundLogger.class);
    //环绕增强方法
    public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
        log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
        try {
            Object result = jp.proceed();
            log.info("调用 " + jp.getTarget() + "的"+jp.getSignature().getName() +"方法。方法返回值:" + result);
            return result;
        } catch (Throwable e) {
            log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
            throw e;
        } finally {
            log.info(jp.getSignature().getName() + " 方法结束执行。");
        }
    }
}

3.配置切面


 
 
 
        
        
        
            
            
        
 

4.测试

@org.junit.Test
public void testAroundLogger() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService service = ctx.getBean("service", UserService.class);
        User user = new User();
        user.setId(1);
        user.setUsername("aiden");
        user.setPassword("666666");
        user.setEmail("aiden@163.com");
        service.save(user);
}

5.运行结果

三、依赖注入 3.1.构造注入

问题:

  • 除了使用set方法,还有什么方式实现注入?

分析:

  • 可以使用带参构造
  • Spring通过构造方法为属性赋值的一种注入方式
  • 可以在对象初始化时对属性赋值,具有良好的时效性

语法:


	

注意:

  • 编写带参构造方法后,Java虚拟机不再提供默认的无参构造方法,为了保证使用的灵活性,建议自行添加一个无参构造方法
3.2.Set 注入 (重点)

注意:被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is

Address.java

package com.aiden.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

Student.java

package com.aiden.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;


public class Student {
    private String name;//姓名
    private Address address;//地址
    private String[] books;//喜欢的书籍
    private List hobbys;//爱好
    private Map card;//银行卡
    private Set games;//喜欢的游戏
    private String wife;
    private Properties info;

    public void setName(String name) {
        this.name = name;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public void setHobbys(List hobbys) {
        this.hobbys = hobbys;
    }

    public void setCard(Map card) {
        this.card = card;
    }

    public void setGames(Set games) {
        this.games = games;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public void show() {
        System.out.println("name=" + name
                + ",address=" + address.getAddress()
                + ",books="
        );
        for (String book : books) {
            System.out.print("<<" + book + ">>t");
        }
        System.out.println("n爱好:" + hobbys);

        System.out.println("card:" + card);

        System.out.println("games:" + games);

        System.out.println("wife:" + wife);

        System.out.println("info:" + info);
    }
}
3.3扩展的注入

3.3.1.常量注入

     

 @Test
 public void test01(){
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
     Student student = context.getBean("student",Student.class);
     System.out.println(student.getName());
 }
3.3.2Bean注入
 
     
 
 
 
     
     
     
 
3.3.3数组注入
 
     
     
     
         
             西游记
             红楼梦
             水浒传
         
     
 
3.3.4List注入
 
     
         阅读
         看电影
         听歌
     
 
3.3.5Map注入
 
     
         
         
     
 	
3.3.6set注入
 
     
         LOL
         王者荣耀
         第五人格
     
 
3.3.7Null注入
 	
3.3.8Properties 注入
 
     
         2021001
         
         小明
     
 
3.3.9P命名和C命名注入
 public class User {
     private String name;
     private int age;
 
     public void setName(String name) {
         this.name = name;
    }
 
     public void setAge(int age) {
         this.age = age;
    }
 
     @Override
     public String toString() {
         return "User{" + "name='" + name + ''' + ", age=" + age +'}';
     }
   }

​ 3.9.1、P命名空间注入 : 需要在头文件中加入约束文件

​ 语法:






 

​ 3.9.2、C 命名空间注入 : 需要在头文件中加入约束文件




四、使用注解实现Spring IoC 4.1.使用注解实现Spring IoC

注解方式将Bean的定义信息和Bean实现类结合在一起,Spring提供的注解有

@Component:实现Bean组件的定义

@Repository:用于标注DAO类

@Service:用于标注业务类

@Controller:用于标注控制器类

//与在XML配置文件中编写等效
 @Repository("userDao")
 public class UserDaoImpl implements UserDao {
	…
 }
4.2.使用注解实现Spring IoC
  • 使用 @Autowired 注解实现Bean的自动装配

  • 默认按类型匹配,可使用 @Qualifier 指定Bean的名称

  • 当dao接口有多个实现容易引发错误,可借助==@Qualifier==来区分实现

@Service("userService")
public class UserServiceImpl implements UserService {
	@Autowired // 默认按类型匹配
	@Qualifier("userDao")
	private UserDao dao;
	……
}
4.3.使用注解实现Spring IoC

修改配置文件使注解生效,完成Bean的定义和组装



	
	

五、使用Java标准注解完成装配 5.1.使用Java标准注解完成装配2-1
  • 使用 @Resource 注解实现组件装配
  • 默认按名称匹配
@Service("userService")
public class UserServiceImpl implements UserService {
	@Resource// 查找名为dao的Bean,并注入给dao属性
	private UserDao dao;
	……
}
@Service("userService")
public class UserServiceImpl implements UserService {
	@Resource(name="userDao")// 为dao属性注入名为userDao的Bean
	private UserDao dao;
	……
}
5.2.使用Java标准注解完成装配2-2

示例:

@Service("userService")
public class UserServiceImpl implements UserService {
	@Resource(type = UserDaoImpl.class)// 用于显示指定依赖的Bean的具体类型
	private UserDao dao;
	……
}
  • 对比 @Autowired 注解和 @Resource 注解
    • @Autowired是Spring提供的注解,@Resource 是Java 提供的
    • 在不指定任何参数且不配合其他注解时,@Autowired注解会优先按Bean的类型进行装配,@Resource注解则是优先按Bean的名称进行装配
六、使用注解实现Spring AOP 6.1.使用注解实现Spring AOP

AspectJ

  • 面向切面的框架,它扩展了Java语言,定义了AOP语法,能够在编译期提供代码的织入

@AspectJ

  • AspectJ 5新增的功能,使用JDK 5.0注解技术和正规的AspectJ切点表达式语言描述切面

Spring通过集成AspectJ框架实现了以注解的方式定义切面,使得配置文件的代码大大减少

  • 利用轻量级的字节码处理框架asm处理@AspectJ中所描述的方法参数名

**注意:**使用==@AspectJ==,首先要保证所用的JDK是5.0或以上版本

实现AOP的切面主要有以下几个要素

  1. 使用@Aspect注解将一个java类定义为切面
  2. 使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等
  3. 使用@Before在切入点开始处切入内
  4. 使用@After在切入点结尾处切入内
  5. 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
  6. 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内
  7. 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
6.2.使用注解实现Spring AOP

需求说明

  • 使用注解方式实现对业务方法前后增加日志

  • 使用注解定义前置增强和后置增强实现日志功能
    @Aspect
    @Before
    @AfterReturning

  • 编写Spring配置文件,完成切面织入

:启用对于@AspectJ注解的支持

实现步骤

1.创建UserServiceLogger类使用注解定义切面

package com.aiden.aop;

import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;


@Aspect
public class UserServiceLogger {
	private static final Logger log = Logger.getLogger(UserServiceLogger.class);

	@Pointcut("execution(* com.aiden.service.UserService.*(..))")
	public void pointcut() {}

	
	@Before("pointcut()")
	public void before(JoinPoint jp) {
		log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
				+ " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
	}

	
	@AfterReturning(pointcut = "pointcut()", returning = "returnValue")
	public void afterReturning(JoinPoint jp, Object returnValue) {
		log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
				+ " 方法。方法返回值:" + returnValue);
	}
}

2.Dao实现

package com.aiden.dao.impl;

import org.springframework.stereotype.Repository;

import com.aiden.dao.UserDao;
import com.aiden.entity.User;


@Repository
public class UserDaoImpl implements UserDao {
	
	public int saveUser(User user) {
		// 这里并未实现完整的数据库操作,仅为说明问题
		System.out.println("保存用户信息到数据库");
		return 1;
	}
}

3.service实现类

package com.aiden.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.aiden.service.UserService;
import com.aiden.dao.UserDao;
import com.aiden.entity.User;


@Service("userService")
public class UserServiceImpl implements UserService {

	// 声明接口类型的引用,和具体实现类解耦合
 @Autowired
	private UserDao dao;

	
	public int save(User user) {
		// 调用用户DAO的方法保存用户信息
		return dao.saveUser(user);
	}
}

4.spring核心配置



	
 
	
	

七、使用注解定义其他类型的增强 7.1.使用注解定义异常抛出增强

问题:

  • 使用注解来定义异常抛出增强

分析:

  • 使用@AfterThrowing注解定义异常抛出增强
@Repository
public class UserDaoImpl implements UserDao {
	public void saveUser(User user) {
		System.out.println("保存用户信息到数据库");
		throw new RuntimeException("为测试程序运行效果抛出的异常");
	}
}
@Service("userService")
public class UserServiceImpl implements UserService {
	// 声明接口类型的引用,和具体实现类解耦合
	@Autowired
 private UserDao dao;
	public void save(User user) {
		// 调用用户DAO的方法保存用户信息
		dao.saveUser(user);
	}
}
package com.aiden.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;


@Aspect
public class ErrorLogger {
 private static final Logger log = Logger.getLogger(ErrorLogger.class);
	
 @AfterThrowing(pointcut = "execution(* com.aiden.service.UserService.*(..))", throwing = "e")
	public void afterThrowing(JoinPoint jp, RuntimeException e) {
		log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
	}
}


 
	
	
	

7.2.使用注解定义最终增强

问题:

  • 使用注解来定义最终增强

分析:

  • 使用@After注解定义最终增强
@Repository
public class UserDaoImpl implements UserDao {
	public void saveUser(User user) {
		System.out.println("保存用户信息到数据库");
	}
}
@Service("userService")
public class UserServiceImpl implements UserService {
	// 声明接口类型的引用,和具体实现类解耦合
	@Autowired
 private UserDao dao;
	public void save(User user) {
		// 调用用户DAO的方法保存用户信息
		dao.saveUser(user);
	}
}
package com.aiden.aop;

import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;


@Aspect
public class AfterLogger {
	private static final Logger log = Logger.getLogger(AfterLogger.class);

	@After("execution(* com.aiden.service.UserService.*(..))")
	public void afterLogger(JoinPoint jp) {
		log.info(jp.getSignature().getName() + " 方法结束执行。");
	}
}



7.3.使用注解定义环绕增强

问题:

  • 如何使用注解来定义环绕增强?

分析:

  • 使用@Around注解定义环绕增强
package com.aiden.aop;

import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;


@Aspect
public class AroundLogger {
	private static final Logger log = Logger.getLogger(AroundLogger.class);

	@Around("execution(* com.aiden.service.UserService.*(..))")
	public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
		log.info("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
				+ " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
		try {
			Object result = jp.proceed();
			log.info("调用 " + jp.getTarget() + " 的 "
					+ jp.getSignature().getName() + " 方法。方法返回值:" + result);
			return result;
		} catch (Throwable e) {
			log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
			throw e;
		} finally {
		    log.info(jp.getSignature().getName() + " 方法结束执行。");
		}
	}
}



7.4.注解使用小结

Spring在同一个功能上提供了注解和配置文件两种实现方式以供选择
通常情况下,优先选择注解来简化配置工作量
常用的注解
@AfterThrowing
@Before
@AfterReturning
@Around
@After
@Aspect
@Pointcut

八、本章总结

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/831043.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号