Spring笔记
Spring
Spring5框架概述
- 轻量级(引入jar包少,体积小)、开源的(免费提供源代码)针对J2EE的框架
- 目的是为了解决企业应用开发复杂性
- 包括控制反转(IOC)和面向切面(AOP)
Spring特点
- 方便解耦,简化开发
- AOP编程支持
- 方便程序测试
- 方便和其他框架整合
- 方便事务操作
- 降低API开发难度
IOC容器
将创建对象的过程交给Spring进行管理,这么一个过程称为“控制反转”
不用再内部new,而是将常见对象的控制权交给IOC容器实现,用的时候直接拿即可
将对象的创建和对象的调用过程,交给Spring进行管理
AOP
在不修改源代码的情况下,进行功能添加,这样一个功能称为“面向切面”
相关jar包文件
https://repo.spring.io/ui/native/release/org/springframework/spring/5.3.22/
-
创建一个基本的spring需要导入以下几个jar包
spring-beans
spring-core
spring-context
spring-expression
commons-logging
IOC简单底层原理
beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.dragon.pojo.User"/>
<!-- <bean id="user" class="com.dragon.pojo.User"/>-->
<!-- <bean id="user" class="com.dragon.pojo.User"/>-->
</beans>
-
工厂模式
public class ClassPathXmlApplicationContext implements ApplicationContext{ /* 1.创建IOC容器 String 对应getBean()中的参数 Object 对应具体创建的类 */ private Map<String,Object> iocContainer = new HashMap<>(); /* 2.构造器创建 */ public ClassPathXmlApplicationContext(String xmlPath){ /* 3.Dom4j解析xml配置文件中的bean并返回List集合 其中Bean是一个封装了xml中bean的id、class的实体类 */ List<Bean> listBean = getListBean(xmlPath); /* 4.创建对象(反射) */ createObject(listBean); } //这里就是getBean(id) @Override public Object getBean(String id) { return iocContainer.get(id); } }
-
Dom4J解析XML
private List<Bean> getListBean(String xmlPath) throws DocumentException{ SAXReader saxReader = new SAXReader(); //读配置文件 Document document = saxReader.read("src/main/resources/" + xmlPath); //获取根节点元素<beans> Element rootElement = document.getRootElement(); //获取子节点集合 Iterator<Element> elementIterator = rootElement.elementIterator(); ArrayList<Bean> beans = new ArrayList<>(); //迭代子节点集合 while (elementIterator.hasNext()){ Bean bean = new Bean(); Element next = elementIterator.next(); //获取bean中的id值 String id = next.attributeValue("id"); bean.setId(id); //获取bean中的class值 String clazz = next.attributeValue("class"); bean.setClassPath(clazz); beans.add(bBean); } return beans; }
-
反射机制
public void createObject(List<Bean> beans){ try{ Iterator<Bean> iterator = beans.iterator(); while(iterator.hasNext()){ Bean bean = iterator.next(); //获取类路径 String classPath = bean.getClassPath(); //反射机制获取类 Class aClass = Class.forName(classPath); //实例化 Object instance = aClass.newInstance(); //将id和实例化后的内容放入容器中 iocContainer.put(bean.getId(), instance); } }catch (Exception e){ e.printStackTrace(); } }
Bean的实例化
1.构造器实例化
2.静态工厂
/**
* 静态工厂模式
* (解耦)
*/
public class DaoFactory {
public static UserDao userDaoFactory(){
return new UserDao();
}
}
<bean id="daoFactory" class="com.dragon.factory.DaoFactory" factory-method="userDaoFactory"/>
3.实例化工厂
/**
* 实例化工厂Bean
*/
public class DaoFactory01 implements FactoryBean<UserDao> {
@Override
public UserDao getObject() throws Exception {
return new UserDao();
}
@Override
public Class<?> getObjectType() {
return UserDao.class;
}
}
<bean id="daoFactory01" class="com.dragon.factory.DaoFactory01"/>
Bean的作用域
共有五种作用域
- singleton:单例模式,只有一个实例
- prototype:全局模式,每次new都会创建一个新的实例
- request:每次请求创建实例
- session:在httpsession中有效
- global-session:全局session
Bean的生命周期
-
什么是生命周期?
从对象创建到对象销毁
-
bean的生命周期
-
初始化容器
1.创建对象(内存分配),相当于new
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法 -
使用bean
1.执行业务操作
-
关闭/销毁容器
1.执行bean销毁方法
-
-
演示bean的生命周期
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bean01" class="com.dragon.pojo.User" init-method="initMethod" destroy-method="destroyMethod"> <property name="id" value="3"/> <constructor-arg ref="teacher"/> </bean> <bean id="teacher" class="com.dragon.pojo.Teacher"/> </beans>
public class TestApplication { public static void main(String[] args) { /* 1.创建bean 实例 2.set注入 3.初始化构造器 4.获取bean对象 5.bean销毁 */ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean01.xml"); User bean01 = context.getBean("bean01",User.class); System.out.println(bean01.getId()); bean01.destroyMethod(); } }
-
实现接口InitializingBean和DisposableBean,会默认执行初始化bean和销毁bean
不过要注意的是,必须使用ClassPathXmlApplicationContext
同时若执行销毁方法,是有两种执行方法的
-
ClassPathXmlApplicationContext.close();
强制关闭,不建议使用
-
ClassPathXmlApplicationContext.registerShutdownHook();
推荐使用这种注册关闭,因为当JVM关闭时,第一件事就是先去把容器关闭,再去关闭JVM
public class UserDao implements InitializingBean, DisposableBean { @Override public void destroy() throws Exception { System.out.println(this.getClass().getName()+" 销毁"); } /** * 在属性设置之后 * @throws Exception */ @Override public void afterPropertiesSet() throws Exception { System.out.println(this.getClass().getName() + " set-init"); } private UserService userService; public UserDao(){ System.out.println("构造器初始化"); } public void setUserService(UserService userService){ System.out.println("-----------set依赖注入"); this.userService = userService; } public void selectOne(){ System.out.println("selectOne"); } } /** 输出结果: 构造器初始化 -----------set依赖注入 com.dragon.dao.UserDao set-init selectOne com.dragon.dao.UserDao 销毁 **/
-
Spring循环依赖
3种形态
-
相互依赖
类A依赖B,类B依赖A
class A{ @Autowired private B b; } class B{ @Autowired private A a; }
-
三者依赖
类A依赖B,类B依赖C,类C依赖A
class A{ @Autowired private B b; } class B{ @Autowired private C c; } class C{ @Autowired private A a; }
-
自身依赖
类A依赖A
public class A{ @Autowired private A a; }
解决循环依赖方法
-
共有三种缓存
- 一级缓存:存放可以被用户使用的bean(被实例化的)
- 二级缓存:存放创建好,但没有完全初始化完的属性的bean集合
- 三级缓存:存放正在被创建的bean集合,代理bean,比如AOP
DI-注入方式
-
什么是依赖注入
在容器中建立bean与bean之间的依赖关系的过程,称为“依赖注入”
-
三种注入方式
-
构造器注入
<constructor-arg ref="teacher"/>
-
set注入
<property name="teacher" ref="teacher"/>
-
注解注入
使用了反射中的暴力反射
@Autowired private Teacher teacher; @Be
-
自动装配
-
什么是自动装配?
所谓自动装配就是往IOC容器中以什么样的方式去注入bean
-
注解使用
- @Autowired:默认按照byType装配
- @Qualifired:配合AutoWired使用指定Name
- @Resource:默认按照name查找,查找不到,按照type查找
装配规则
- byName:按照名称自动装配
- byType:按照类型自动装配
- constructor:构造器中的参数装配
AOP底层原理
AOP是什么
面向切面
为什么会有AOP?
在不改变源代码的基础上,去增加一些新的功能
AOP核心概念
-
连接点(JoinPoint)
被拦截的点,指方法
-
切入点(PointCut)
被拦截后执行的增强功能,指方法
-
切面(Aspect)
描述切入点和通知的对应关系
-
通知(Advice)
在切入点执行的操作
-
通知类
例子:
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.dragon.service.impl.UserServiceImpl.add())")
private void pt(){
}
@Before("pt()")
public void myAdvice(){
System.out.println(System.currentTimeMillis());
}
}
那么在这个操作里面就用到了动态代理,其中包括代理对象和目标对象。
- 目标对象:指原来的对象,也就是被代理的对象
- 代理对象:就是替代原来对象的对象,去执行
输出内容:
public class MyApplicationTest {
@Test
public void test01(){
ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
UserService bean = context.getBean("userServiceImpl", UserService.class);
System.out.println(bean);
System.out.println(bean.getClass());
}
}
/*
bean: com.dragon.service.impl.UserServiceImpl@776b83cc
bean.getClass():class com.sun.proxy.$Proxy21
*/
动态代理
-
有接口
使用JDK动态代理:实现接口代理对象
-
无接口
使用CGLIB动态代理:创建子类代理对象
JDK动态代理
使用了java.lang.reflect
包中的Proxy类里面的newProxyInstance方法
其中该方法包含三个参数
- 类加载器
- 类的实现接口
- 创建代理对象(实现InvocationHandler接口),写增强部分
-
代理对象
public class JdkProxy { public static void main(String[] args) { //接口 Class[] interfaces = {UserDao.class}; //被切入的类 UserDaoImpl userDao = new UserDaoImpl(); /* java.lang.reflect下的Proxy类 调用newProxyInstance方法去实现AOP操作 */ UserDao dao = (UserDao) Proxy.newProxyInstance( JdkProxy.class.getClassLoader(),interfaces,new UserDaoProxy(userDao)); int res = dao.add(1, 2); System.out.println(res); } }
-
实现InvocationHandler接口
public class UserDaoProxy implements InvocationHandler { private Object obj; //构造函数,传入要操作的类 public UserDaoProxy(Object obj){ this.obj = obj; } /* proxy:被代理对象 method:被代理对象的方法Method对象 args:被代理对象的某方法接收的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName()+"方法执行前"); Object invoke = method.invoke(obj, args); System.out.println(method.getName()+"方法执行之后"); return invoke; } }
AOP切入点表达式
访问修饰符 返回值类型 包路径.类名.方法名(参数) 异常
- ..
AOP通知类型
AOP注解
@Aspect
-
@Before
在被代理目标方法之前执行
-
@After
在被代理目标方法之后执行
-
@AfterReturning
在被代理目标方法完成之后执行
-
@AfterThrowing
在被代理目标方法执行中出现异常执行
-
@Around
@PointCut():抽取方法
@Component
@Aspect
public class UserProxy {
/**
* 相同切入点抽取
*/
//抽取方法
@Pointcut(value = "execution(* com.dragon.aop.User.add(..))")
public void commonTest(){
}
//调用commonTest()相当于调用了User中的add方法
@Before(value = "commonTest()")
public void before(){
System.out.println("执行之前");
}
@After(value = "commonTest()")
public void after(){
System.out.println("执行之后");
}
@AfterReturning(value = "commonTest()",returning = "obj")
public void afterReturning(JoinPoint joinPoint,Object obj){
Signature signature = joinPoint.getSignature();
System.out.println("afterReturning");
System.out.println(signature.getName()+obj);
}
//出现异常抛出
@AfterThrowing(value = "commonTest()",throwing = "ex")
public void afterThorowingAdviceMethod(JoinPoint joinPoint,Exception ex){
//获取连接点所对应方法的签名信息
Signature signature = joinPoint.getSignature();
System.out.println("LoggerAspect,方法:"+signature.getName()+",异常:"+ex);
}
@Around(value = "commonTest()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("=====环绕之前======");
joinPoint.proceed();
System.out.println("======环绕之后=======");
}
}
调用目标参数
@Before("ptGetArgs()")
public void myAdvice(JoinPoint jp){
Object[] args = jp.getArgs();//关键代码
System.out.println(Arrays.toString(args));
System.out.println(System.currentTimeMillis());
}
Spring事务
- 什么是事务?
例如银行转账,在一个业务类中,有两个数据库的操作,A向B转200,A少200,B多200。这样一个操作就是事务
如若操作成功,即成功,若其中一个失败则会进行事务回滚
- 关键注解
@Transactional
该注解最标准的使用应该操作在接口中的方法上或类上,表名该方法中被事务所管理
- 设置事务管理器
在配置类中设置,
@Bean
public PlatformTranscationManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ptm = new DataSourceTransactionManager();
//此处dataSource为Druid的数据源
ptm.setDataSource(dataSource);
return ptm;
}
-
开启注解事务驱动
@EnableTransactionManagement
操作在配置类上
事务角色
-
事务管理员
就是被 @Transactional操作的方法为事务管理员
-
事务协调员
本来为单个事务,但是因为被事务管理员所管理,所以改成了事务协调员