Spring框架(IOC,DI,AOP)
Spring框架是Java开源项目的一员,可提高项目的开发效率,其框架中有多个模块,包括:
- 核心容器
- Spring-AOP
- Spring Data Access(数据访问)
- Web模块
- 报文发送
- 单元测试
需要了解的重点是核心容器中的IOC(控制反转)和DI(依赖注入)以及Spring-AOP。
首先我们需要搭建整个框架的配置文件,新建maven项目后在pom.xml文件中需要添加以下依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
同时在配置spring.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--此处添加后续需要使用的内容-->
</beans>
1. Spring IOC容器 Bean对象实例化
先构建UserDao类javabean以及静态工厂类StaticFactory类和实例化工厂类UserFactory类
定义UserDao类:
public class UserDao {
private String uname;
private String pwd;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
定义静态工厂类:
import com.gao.dao.UserDao;
public class StaticFactory {
public static UserDao getUserDao(){
return new UserDao();
}
}
定义实例化工厂类:
import com.gao.dao.UserDao;
public class UserFactory {
public UserDao getUserDao(){
return new UserDao();
}
}
对象实例化方式有三种:
- 构造器实例化:
<bean id="userDao" class="com.gao.dao.UserDao"></bean>
- 静态工厂实例化:
<bean id="userDao" class="com.gao.factory.StaticFactory" factory-method="getUserDao"></bean>
- 实例化工厂实例化:
<bean id="userFactory" class="com.gao.factory.UserFactory"></bean>
<bean id="userDao" factory-bean="userFactory" factory-method="getUserDao"></bean>
测试方法:
@Test
public void test(){
ApplicationContext app = new ClassPathXmlApplicationContext("spring.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
System.out.println(userDao);
userDao.dao();
}
2. Spring IOC注入
2.1 手动注入
Spring支持的手动注入方式有四种:set 注⼊、构造器注⼊、静态⼯⼚注⼊、实例化⼯⼚注⼊
定义UserDao类:
public class UserDao {
private String uname;
private String pwd;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
定义UserService02类:
public class UserService02 {
private UserDao userDao;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
定义TestDao类:
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class TestDao {
private List<Integer> list;
private Set<Integer> set;
private Map<Integer,String> map;
private Properties prof;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
2.1.1 set注入
推荐使用set注入,前提是属性字段需要提供set方法
如果类的成员为常用对象和基本类型:
<bean id="userDao" class="com.gao.dao.UserDao">
<property name="uname" value="zhangsan"></property>
<property name="pwd" value="123456"></property>
</bean>
如果类的成员为javabean:
<bean id="userDao" class="com.gao.dao.UserDao"></bean>
<bean id="userService02" class="com.gao.service.UserService02">
<property name="userDao" ref="userDao"></property>-->
</bean>
如果类的成员为集合类型:
<bean id="testDao" class="com.gao.dao.TestDao">
<property name="list">
<list>
<value>2</value>
<value>3</value>
<value>4</value>
</list>
</property>
<property name="set">
<set>
<value>1</value>
<value>5</value>
<value>2</value>
</set>
</property>
<property name="map">
<map>
<entry>
<key><value>1</value></key>
<value>zhangsan</value>
</entry>
<entry>
<key><value>3</value></key>
<value>lisi</value>
</entry>
<entry>
<key><value>2</value></key>
<value>wangwu</value>
</entry>
</map>
</property>
<property name="prof">
<props>
<prop key="username">haha</prop>
<prop key="pwd">123456</prop>
</props>
</property>
</bean>
2.1.2 构造器注入
实现构造器注入前提是提供带参构造器
如果类的成员为常用对象和基本类型:
<bean id="userDao" class="com.gao.dao.UserDao">
<constructor-arg index="0" value="lisi"></constructor-arg>
<constructor-arg index="1" value="123"></constructor-arg>
</bean>
如果类的成员为javabean:
<bean id="userDao" class="com.gao.dao.UserDao"></bean>
<bean id="userService02" class="com.gao.service.UserService02">
<constructor-arg index="0" ref="userDao"></constructor-arg>
</bean>
注:如果出现循环依赖即两个类互相将对方作为成员,此时需要使用set注入
2.1.3 静态工厂注入
使用静态工厂实例化类中的bean对象成员,使用set注入
<bean id="userDao" class="com.gao.factory.StaticFactory" factory-method="getUserDao"></bean>
<bean id="userService02" class="com.gao.service.UserService02">
<property name="userDao" ref="userDao"></property>-->
</bean>
2.1.4 实例化工厂注入
使用实例化工厂实例化类中的bean对象成员,使用set注入
<bean id="userFactory" class="com.gao.factory.UserFactory"></bean>
<bean id="userDao" factory-bean="userFactory" factory-method="getUserDao"></bean>
<bean id="userService02" class="com.gao.service.UserService02">
<property name="userDao" ref="userDao"></property>-->
</bean>
2.2 自动注入
定义UserDao类:
public class UserDao {
private String uname;
private String pwd;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
定义UserService类:
public class UserService {
private UserDao userDao;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
开启自动化注入:
<context:annotation-config/>-->
<bean id="userDao" class="com.gao.dao.UserDao"></bean>-->
<bean id="userService" class="com.gao.service.UserService"></bean>-->
2.2.1 @Resource注解
推荐使用@Resource注解
- 默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
- 如果属性字段名称未找到,则会通过类型(Class类型)查找
- 属性可以提供set⽅法,也可以不提供set⽅法
- 注解可以声明在属性上或set⽅法上
- 可以设置name属性,name属性值必须与bean标签的id属性值⼀致;如果设置了name属性值,就只会按照name属性值查找bean对象
- 当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤name属性指定需要被实例化的bean对象
用法:
public class UserService {
//name可省略
@Resource(name = "userDao")
private UserDao userDao;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
2.2.2 @Autowired注解
- 默认通过类型(Class类型)查找bean对象,与属性字段的名称⽆关
- 属性可以提供set⽅法,也可以不提供set⽅法
- 注解可以声明在属性上或set⽅法上
- 可以添加@Qualifier结合使⽤,通过value属性值查找bean对象(value属性值必须要设置,且值要与bean标签的id属性值对应)
用法:
public class UserService {
//@Qualifier可省略
@Autowired
@Qualifier(value="userDao")
private UserDao userDao;
//空构造,带参构造,get和set方法,toString方法就不写出来了
}
2.3 IOC扫描器
使用IOC扫描器后不用再设置bean标签,设置自动化扫描范围:
<context:component-scan base-package="com.gao"/>
注解有以下几种:
- @Repository(Dao层)
- @Service(Service层)
- @Controller(Controller层)
- @Component(任意层)
3. Spring AOP
AOP即面向切面编程,用来拦截整个面的功能,主要用于⽇志记录,性能统计,安全控制,事务处理等⽅⾯,实现公共功能性的重复使⽤。
AOP的底层实现:动态代理(JDK+CGLIB)
3.1 AOP的基本概念
- Joinpoint(连接点):spring中指被拦截到的每一个方法
- Pointcut(切⼊点):匹配规则定义拦截哪些方法,对哪些方法进行处理
- Advice(通知):拦截到方法后要做的操作,包括:
- 前置通知:before 执行方法前通知
- 返回通知:afterReturn 方法正常结束返回后的通知
- 异常抛出通知 afterThrow
- 最终通知:after 无论方法是否发生异常,均会执行该通知
- 环绕通知:around 可以在方法调用前后完成自定义行为,也会选择是否继续执⾏连接点或直接返回它们⾃⼰的返回值或抛出异常来结束执⾏
- Aspect(切⾯):切入点和通知的结合,与类相似,是对横切关注点的抽象
- Target(⽬标对象):被代理的目标对象
- Weave(织⼊):将切面应用到目标对象并生成代理对象的过程
- Introduction(引⼊):在不修改源码的前提下,在程序运⾏期为类动态添加⽅法或者字段的过程
3.2 注解实现AOP
AOP切入点表达式简介:
- 执⾏任意公共⽅法:execution(public *(…))
- 执⾏任意的set⽅法:execution(* set*(…))
- 执⾏com.xxxx.service包下任意类的任意⽅法:execution(* com.xxxx.service..(…))
- 执⾏com.xxxx.service 包以及⼦包下任意类的任意⽅法:execution(* com.xxxx.service….(…))
注:表达式中第一个*是方法的修饰符
配置文件:
<!--配置AOP代理-->
<aop:aspectj-autoproxy/>
定义切面的方法一:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAop {
//定义切点
@Pointcut("execution(* com.gao.controller.UserController.*(..))")
public void cut(){}
//声明前置通知
@Before(value = "cut()")
public void aopBefore(){
System.out.println("before...");
}
//声明返回通知
@AfterReturning(value = "cut()")
public void aopAfterReturn() {
System.out.println("返回通知.....");
}
//声明最终通知
@After(value = "cut()")
public void aopAfter(){
System.out.println("after...");
}
//声明异常通知
@AfterThrowing(value="cut()",throwing = "e")
public void aopAfterThrow(Exception e) {
System.out.println("异常通知....." + " 异常原因:" + e.getCause());
}
//声明环绕通知
@Around(value = "cut()")
public Object aopAround(ProceedingJoinPoint pjp){
System.out.println("前置通知...");
Object obj = null;
try {
obj = pjp.proceed();
System.out.println(pjp.getTarget()+"-->"+pjp.getSignature());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("后置通知");
return obj;
}
}
定义切面的方法二(将切点与通知结合在一起):
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAop {
@Before("execution(* com.gao.controller.UserController.*(..))")
public void aopBefore(){
System.out.println("before...");
}
@AfterReturning("execution(* com.gao.controller.UserController.*(..))")
public void aopAfterReturn() {
System.out.println("返回通知.....");
}
@After("execution(* com.gao.controller.UserController.*(..))")
public void aopAfter(){
System.out.println("after...");
}
@Around("execution(* com.gao.controller.UserController.*(..))")
public Object aopAround(ProceedingJoinPoint pjp){
System.out.println("前置通知...");
Object obj = null;
try {
obj = pjp.proceed();
System.out.println(pjp.getTarget()+"-->"+pjp.getSignature());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("后置通知");
return obj;
}
}
3.3 XML实现AOP
定义切面:
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
@Component
public class MyAop {
public void cut(){}
public void aopBefore(){
System.out.println("before...");
}
public void aopAfter(){
System.out.println("after...");
}
public Object aopAround(ProceedingJoinPoint pjp){
System.out.println("前置通知...");
Object obj = null;
try {
obj = pjp.proceed();
//System.out.println(pjp.getTarget()+"-->"+pjp.getSignature());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("后置通知...");
return null;
}
public void aopAfterReturn(){
System.out.println("返回通知...");
}
public void aopAfterThrow(Exception e){
System.out.println("异常通知"+"异常原因:"+e.getCause());
}
}
配置xml文件:
<!--配置aop-->
<aop:config>
<!--aop切面-->
<aop:aspect ref="myAop">
<!--定义aop切入点-->
<aop:pointcut id="cut" expression="execution(* com.gao.controller.UserController.*(..))"/>
<!--配置前置通知-->
<aop:before method="aopBefore" pointcut-ref="cut"/>
<!--配置最终通知-->
<aop:after method="aopAfter" pointcut-ref="cut"/>
<!--配置环绕通知-->
<aop:around method="aopAround" pointcut-ref="cut"/>
<!--配置返回通知-->
<aop:after-returning method="aopAfterReturn" pointcut-ref="cut"/>
<!--配置异常通知-->
<aop:after-throwing method="aopAfterThrow" throwing="e" pointcut-ref="cut"/>
</aop:aspect>
</aop:config>