Spring启示录
OCP
软件七大开发原则中最基本, 最核心的原则, 开闭原则: 对扩展开放, 对修改关闭.
条件: 在扩展系统功能的时候, 没有修改以前写好的代码, 就符合OCP原则. 反之不符合.
依赖倒置原则(DIP)
凡是上依赖下的, 都违背了依赖倒置原则.
条件: 上 不再依赖 下, 符合依赖倒置原则.
核心: 倡导面向接口编程, 面向抽象编程, 不要面向具体编程.
目的: 降低程序的耦合度, 提高扩展力.
控制反转 IoC
反转的是两件事: 1.不在程序中采用硬编码来new对象了.
2.不在程序中采用硬编码的方式来维护对象的关系了.
控制反转是一种编程思想(新型的设计模式).
spring框架
功能: 实现了IoC思想, 可以自动new对象和维护对象之间的关系, 是实现了IoC的容器.
依赖注入
含义: 控制反转的实现方式有多种, 其中比较重要的叫做: 依赖注入(Dependency Injection, DI).
方式: 1.set注入(执行set方法给属性赋值). 2.构造方式注入(执行构造方法给属性赋值).
依赖: 对象之间的关系. 注入: 让对象之间产生关系的手段.
spring的jar包
注: 如果你只是想⽤Spring的IoC功能,仅需要引⼊:spring-context即可。将这个jar包添加到classpath当中。如果采⽤maven只需要引⼊context的依赖即可。
<!--Spring6的正式版发布之前,这个仓库地址是需要的-->
<repositories>
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
<!--spring context依赖:使⽤的是6.0.0-M2⾥程碑版-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
</dependencies>
Spring入门程序
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.10</version>
</dependency>
spring配置文件(名字不限, 放在类路径中, 即resources目录下)
模板
<?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">
</beans>
bean标签属性: id: bean的唯一标识, 不能重复. class:类的全限定类名.
<bean id="userBean" class="com.powernode.spring6.bean.User"/>
获取bean实例化对象
ApplicationContext: 接口, 有很多实现类. ClassPathXmlApplicationContext: 专门从类路径中加载spring配置文件的spring上下文对象. 原理: 默认spring会通过反射机制, 通过类的无参数构造方法来实例化对象.
反射代码
Class clazz = Class.forName("com.powernode.spring6.bean.User");
Object user = clazz.newInstance();
spring实例化bean
//1.获取spring容器对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
//2.根据bean的id从spring容器中获取这个对象
Object userBean = applicationContext.getBean("userBean");
容器可加载多个spring配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring6.xml", "beans.xml", "xml/beans.xml");
可配置JDK的类
<bean id="nowTime" class="java.util.Date"/>
指定bean类型
Date nowTime = applicationContext.getBean("nowTime", Date.class);
绝对路径加载配置文件
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("F:/AA-studyAgain/A-Spring6/spring6.xml");
注: ApplicationContext的顶级父接口是BeanFactory, BeanFactory也是IoC容器的顶级接口, Spring的IoC用的是工厂模式. Spring的IoC的实现方式: XML解析 + 工厂模式 + 反射机制 注: spring不是在调用getBean时才创建bean, 执行以下代码的时候, 就会实例化对象.
BeanFactory applicationContext = new ClassPathXmlApplicationContext("spring6.xml");
spring6启用Log4j2日志框架
依赖
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
配置文件
日志级别: ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<loggers>
<!--
level指定⽇志级别,从低到⾼的优先级:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
-->
<root level="DEBUG">
<appender-ref ref="spring6log"/>
</root>
</loggers>
<appenders>
<!--输出⽇志信息到控制台-->
<console name="spring6log" target="SYSTEM_OUT">
<!--控制⽇志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
</appenders>
</configuration>
使用
Logger logger = LoggerFactory.getLogger(FirstSpringTest.class);
logger.info("111");
logger.debug("debug11");
logger.error("error22");
Spring对IoC的实现: 依赖注入
set注入
ref: 要注入的bean的id. name: setXxx方法名去掉set切首字母小写的字符串.
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="mySQLUserDao" ref="userDaoBean"/>
</bean>
构造注入
index: 构造方法第i个参数(从0开始). ref: 要注入的bean的id.
<bean id="csBean" class="com.powernode.spring6.service.CustomerService">
<constructor-arg index="0" ref="userDaoBean"/>
<constructor-arg index="1" ref="vipDaoBean"/>
</bean>
name: 构造方法参数名.
<bean id="csBean2" class="com.powernode.spring6.service.CustomerService">
<constructor-arg name="userDao" ref="userDaoBean"/>
<constructor-arg name="vipDao" ref="vipDaoBean"/>
</bean>
spring自动根据类型注入
<bean id="csBean3" class="com.powernode.spring6.service.CustomerService">
<constructor-arg ref="vipDaoBean"/>
<constructor-arg ref="userDaoBean"/>
</bean>
set注入专题
注入外部bean
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<property name="orderDao" ref="orderDaoBean"/>
</bean>
注入内部bean
<bean id="orderServiceBean2" class="com.powernode.spring6.service.OrderService">
<property name="orderDao">
<bean class="com.powernode.spring6.dao.OrderDao"/>
</property>
</bean>
注入简单类型
private int age;
private Integer age2;
private boolean flag;
private Boolean flag2;
private char c;
private Character c2;
private Season season;
private String username;
private Class clazz;
<bean id="svt" class="com.powernode.spring6.bean.SimpleValueType">
<property name="age" value="1"/>
<property name="age2" value="1"/>
<property name="username" value="zhansgan"/>
<property name="season" value="SPRING"/>
<property name="flag" value="false"/>
<property name="flag2" value="true"/>
<property name="c2" value="男"/>
<property name="c" value="男"/>
<property name="clazz" value="java.lang.String"/>
</bean>
Date类型注入
虽然Date是简单类型, 但在实际开发中, 一般不会把它当简单类型. 一般会采用ref给Date类型赋值.
<property name="birth" value="Wed Aug 30 15:29:08 CST 2023"/>
级联属性赋值
赋值属性需提供get方法.
注: 赋值顺序不能颠倒.
public Clazz getClazz() {
return clazz;
}
<bean id="studentBean" class="com.powernode.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
<property name="clazz.name" value="高三二班"/>
</bean>
<bean id="clazzBean" class="com.powernode.spring6.bean.Clazz"/>
注入数组
<bean id="yuQian" class="com.powernode.spring6.bean.QianDaYe">
<property name="aiHaos">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<property name="women">
<array>
<ref bean="w1"/>
<ref bean="w2"/>
<ref bean="w3"/>
</array>
</property>
</bean>
List和Set集合注入
<bean id="personBean" class="com.powernode.spring6.bean.Person">
<property name="names">
<list>
<value>张三</value>
<value>李四</value>
<value>张三</value>
</list>
</property>
<property name="addrs">
<set>
<value>北京大兴区</value>
<value>北京海鼎区</value>
<value>北京大兴区</value>
</set>
</property>
</bean>
Map和Properties注入
如果key和value不是简单类型: <entry key-ref="" value-ref=""/>
<property name="phones">
<map>
<entry key="1" value="112"/>
<entry key="2" value="112"/>
<entry key="3" value="111"/>
<entry key="4" value="123"/>
</map>
</property>
Properties注入(key和value只能是String)
<property name="properties">
<props>
<prop key="driber">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">3306</prop>
<prop key="username">111</prop>
</props>
</property>
注入null和空字符串
null
不给属性注入, 属性的默认值就是null.
手动注入null.
<property name="name">
<null/>
</property>
空字符串
<property name="name" value=""/>
<property name="name">
<value/>
</property>
注入的值含有特殊符号
1.使用实体符号代替特殊符号

<property name="result" value="2 < 3"/>
2.使用<![CDATA[]]>
<value><![CDATA[2 < 3]]></value>
p命名空间注入
目的: 简化配置. 底层: set注入.
使用条件: 1. 在XML头部信息中添加p命名空间的配置信息:
xmlns:p="http://www.springframework.org/schema/p"
xmlns:p="http://www.springframework.org/schema/p"
2. p命名空间注⼊是基于setter⽅法的,所以需要对应的属性提供setter⽅法.
<bean id="birthBean" class="java.util.Date"/>
<bean id="dogBean" class="com.powernode.spring6.bean.Dog" p:name="小花" p:age="3" p:birth-ref="birthBean"/>
c命名空间注入
目的: 简化构造方法注入.
使用条件: 1.需要在xml配置⽂件头部添加信息:
xmlns:c="http://www.springframework.org/schema/c"
xmlns:c="http://www.springframework.org/schema/c"
2.需要提供构造⽅法。
使用:
c:_0 : 0是第0个参数
<bean id="peopleBean" class="com.powernode.spring6.bean.People" c:_0="zhangsan" c:_1="30" c:_2="true"/>
util命名空间
作用: 配置复用.
1.引入util命名空间
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
2.使用
<util:properties id="prop">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="username">root</prop>
<prop key="password">1234</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
</util:properties>
<bean id="ds1" class="com.powernode.spring6.jdbc.MyDataSource1">
<property name="properties" ref="prop"/>
</bean>
<bean id="ds2" class="com.powernode.spring6.jdbc.MyDataSource1">
<property name="properties" ref="prop"/>
</bean>
基于XML的自动装配
按名称自动装配(基于set注入)
<bean id="orderService" class="com.powernode.spring6.service.OrderService" autowire="byName"/>
<bean id="orderDao" class="com.powernode.spring6.dao.OrderDao"/>
按类型自动装配(基于set方法)
注: 一种类型只能有一个bean实例
<bean class="com.powernode.spring6.dao.VipDao"/>
<bean class="com.powernode.spring6.dao.UserDao"/>
<bean id="cs" class="com.powernode.spring6.service.CustomerService" autowire="byType"/>
spring引入外部属性配置文件
1.引入context命名空间
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
2.使用
<context:property-placeholder location="jdbc.properties"/>
<bean id="ds" class="com.powernode.spring6.jdbc.MyDataSource">
<property name="driver" value="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
注: ${key}优先取系统变量
Bean的作用域
spring默认单例(启动容器就创建单例对象)
设置为多例模式(每调用一次getBean创建一次对象) scope="prototype"
<bean id="sb" class="com.powernode.spring6.bean.SpringBean" scope="prototype"/>
注: scope属性其他值: request: 一个请求一个对象. session: 一个会话一个对象.
自定义scope
一个线程一个bean
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="threadScope">
<!--spring内置的, 也可自定义-->
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
<bean id="sb" class="com.powernode.spring6.bean.SpringBean" scope="threadScope"/>
GoF之工厂模式
工厂模式三种形态:
1.简单工厂模式(不属于23种模式, 又称静态方法工厂模式, 是工厂方法模式的一种特殊实现).
2.工厂方法模式(属于23种模式). 3.抽象工厂模式(属于23种模式).
简单工厂模式
抽象产品角色
public abstract class Weapon {
public abstract void attack();
}
具体产品角色
public class Tank extends Weapon{
@Override
public void attack() {
System.out.println("坦克开炮!!!");
}
}
工厂类角色
public class WeaponFactory {
public static Weapon get(String weaponType){
return switch (weaponType) {
case "TANK" -> new Tank();
case "DAGGER" -> new Dagger();
case "FIGHTER" -> new Fighter();
default -> throw new RuntimeException("不支持该武器的生产");
};
}
}
优点: 生产者和消费者分离.
缺点: 1.需要扩展产品需要改写工厂类代码, 违背OCP(开闭原则).
2.工厂类负责所有产品的生产, 不能出现任何问题,一旦出问题, 整个系统全部瘫痪.
工厂方法模式
解决简单工厂模式的违背OCP问题: 一个工厂只生产一种产品.
抽象产品角色
public abstract class Weapon {
public abstract void attack();
}
具体产品角色
public class Tank extends Weapon{
@Override
public void attack() {
System.out.println("坦克开炮!!!");
}
}
抽象工厂角色
public abstract class WeaponFactory {
public abstract Weapon get();
}
具体工厂角色
public class TankFactory extends WeaponFactory{
@Override
public Weapon get() {
return new Tank();
}
}
优点: 符合OCP.
缺点:增加系统复杂性.
Bean的实例化方式
1.构造方法实例化
<bean id="sb" class="com.powernode.spring6.bean.SpringBean"/>
2.简单工厂模式实例化
自定义工厂类
public class StarFactory {
public static Star get(){
return new Star();
}
}
配置
<bean id="starBean" class="com.powernode.spring6.bean.StarFactory" factory-method="get"/>
3.factory-bean实例化
具体工厂
public class GunFactory {
public Gun get(){
return new Gun();
}
}
配置
<bean id="gunFactory" class="com.powernode.spring6.bean.GunFactory"/>
<bean id="gun" factory-bean="gunFactory" factory-method="get"/>
4.FactoryBean接口实例化
工厂bean实现FactoryBean<T>接口
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
配置
<bean id="person" class="com.powernode.spring6.bean.PersonFactoryBean"/>
5.BeanFactory和FactoryBean的区别
BeanFactory
Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean⼯⼚”,在Spring的IoC容器中,“Bean⼯
⼚”负责创建Bean对象。
BeanFactory是⼯⼚。
FactoryBean
FactoryBean:它是⼀个Bean,是⼀个能够辅助Spring实例化其它Bean对象的⼀个Bean。
在Spring中,Bean可以分为两类:
第⼀类:普通Bean.
第⼆类:⼯⼚Bean(记住:⼯⼚Bean也是⼀种Bean,只不过这种Bean⽐较特殊,它可以辅助
Spring实例化其它Bean对象。
6.注入自定义Date
public class DateFactoryBean implements FactoryBean<Date> {
private String strDate;
public DateFactoryBean(String strDate) {
this.strDate = strDate;
}
@Override
public Date getObject() throws Exception {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
return simpleDateFormat.parse(strDate);
}
@Override
public Class<?> getObjectType() {
return null;
}
}
<bean id="date" class="com.powernode.spring6.bean.DateFactoryBean">
<constructor-arg index="0" value="2008-10-11"/>
</bean>
<bean id="stu" class="com.powernode.spring6.bean.Student">
<property name="birth" ref="date"/>
</bean>
Bean的生命周期
五步
1.实例化Bean
public User() {
System.out.println("1.无参构造方法执行");
}
2.Bean属性赋值
public void setName(String name) {
System.out.println("2.属性赋值");
this.name = name;
}
3.初始化Bean
public void initBean(){
System.out.println("3.初始化bean");
}
4.使用Bean
5.销毁Bean (必须手动关闭spring容器)
public void destroyBean(){
System.out.println("5.销毁bean");
}
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
context.close();
注: 3和5的方法需要配置
<bean id="user" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="zhangsan"/>
</bean>
七步
基于五步, 在 第三步 之前执行"Bean后处理器的before方法", 之后执行"Bean后处理器的after方法".
bean后处理器类
public class LogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行before");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行after");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
配置 (作用于当前配置文件中的所有bean)
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
十步
基于七步, 在 bean后处理器的before前和后 分别加一步, 和在 使用bean和销毁bean之间 加一步.
before之前: 检查Bean是否实现了Aware相关的接口, 如果实现了接口, 调用接口中方法,
目的: 传递数据, 方便实用.
before之后: 检查Bean是否实现了InitializingBean接口, 如果实现了则调用接口方法.
使用和销毁bean之间: 检查Bean是否实现了DisposableBean接口, 如果实现了则调用接口方法.
作用域不同, 生命周期不同
spring只对singleton的Bean进行完整的生命周期管理.
如果是prototype作用域的Bean, spring容器只负责将该bean初始化完毕, 一旦客户端获取到bean, spring容器将不在管理该对象的生命周期.
将自己new的对象纳入spring容器
Student student = new Student();
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerSingleton("studentBean", student);
Student studentBean = factory.getBean("studentBean", Student.class);
Bean的循环依赖问题
singleton+set注入模式 没问题
原因: 在这种模式下, spring对bean的管理主要分为两个清晰的阶段:
1.spring容器加载, 实例化bean并立即进行曝光.
2.bean曝光之后在进行属性赋值.
<bean id="husbandBean" class="com.powernode.spring6.bean.Husband">
<property name="name" value="张三"/>
<property name="wife" ref="wifeBean"/>
</bean>
<bean id="wifeBean" class="com.powernode.spring6.bean.Wife">
<property name="name" value="小花"/>
<property name="husband" ref="husbandBean"/>
</bean>
prototype + set注入模式 会出异常
BeanCurrentlyInCreationException
singleton + 构造注入 会出异常
BeanCurrentlyInCreationException
Spring源码分析
DefaultSingletonBeanRegistry类下属性:
key: bean的name
一级缓存: private final Map<String, Object> singletonObjects
存储完整的单例bean对象, 属性都已赋值.
二级缓存: private final Map<String, ObjectFactory<?>> earlySingletonObjects
存储早期单例bean对象, 属性没有赋值.
三级缓存: private final Map<String, Object> earlySingletonObjects
存储单例工厂对象, 创建单例bean的单例工厂对象.
回顾反射机制
//获取类
Class<?> clazz = Class.forName("com.powernode.reflect.SomeService");
//获取方法
Method doSomeMethod = clazz.getDeclaredMethod("doSome", String.class, int.class);
//调用方法
Object obj = clazz.newInstance();
Object res = doSomeMethod.invoke(obj, "李四", 250);
System.out.println(res);
//已知
String cn = "com.powernode.reflect.User", pn= "age";
//设置属性
Class<?> clazz = Class.forName(cn);
Field field = clazz.getDeclaredField(pn);
Class<?> type = field.getType();
Method setAge = clazz.getDeclaredMethod("set" + pn.toUpperCase().charAt(0) + pn.substring(1), type);
Object obj = clazz.newInstance();
setAge.invoke(obj, 15);
手写Spring框架
依赖
<dependencies>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
public class ClassPathXmlApplicationContext implements ApplicationContext{
private static final Logger logger = LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);
private Map<String, Object> singletonObjects = new HashMap<>();
public ClassPathXmlApplicationContext(String configLocation) {
try {
SAXReader reader = new SAXReader();
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(configLocation);
Document document = reader.read(in);
//获取所有bean标签
List<Node> nodes = document.selectNodes("//bean");
nodes.forEach(node -> {
try {
Element beanElt = (Element) node;
String id = beanElt.attributeValue("id");
String classname = beanElt.attributeValue("class");
logger.info("beanName=" + id);
logger.info("beanClassName=" + classname);
Class<?> aClass = Class.forName(classname);
Constructor<?> defaultCon = aClass.getDeclaredConstructor();
Object bean = defaultCon.newInstance();
//将bean曝光
singletonObjects.put(id, bean);
logger.info(singletonObjects.toString());
}catch (Exception e){
e.printStackTrace();
}
});
nodes.forEach(node -> {
try {
Element beanElt = (Element) node;
String id = beanElt.attributeValue("id");
String className = beanElt.attributeValue("class");
Class<?> aClass = Class.forName(className);
List<Element> properties = beanElt.elements("property");
properties.forEach(property -> {
try {
String propertyName = property.attributeValue("name");
Field field = aClass.getDeclaredField(propertyName);
logger.info("属性名: " + propertyName);
String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1);
Method setMethod = aClass.getDeclaredMethod(setMethodName, field.getType());
String value = property.attributeValue("value");
Object actualValue = null;
String ref = property.attributeValue("ref");
if (value != null){
String propertySimpleType = field.getType().getSimpleName();
switch (propertySimpleType){
case "byte":
actualValue = Byte.parseByte(value);
break;
case "short":
actualValue = Short.parseShort(value);
break;
case "int":
actualValue = Integer.parseInt(value);
break;
case "long":
actualValue = Long.parseLong(value);
break;
case "float":
actualValue = Float.parseFloat(value);
break;
case "double":
actualValue = Double.parseDouble(value);
break;
case "boolean":
actualValue = Boolean.parseBoolean(value);
break;
case "char":
actualValue = value.charAt(0);
break;
case "Byte":
actualValue = Byte.valueOf(value);
break;
case "Short":
actualValue = Short.parseShort(value);
break;
case "Integer":
actualValue = Integer.valueOf(value);
break;
case "Long":
actualValue = Long.valueOf(value);
break;
case "Float":
actualValue = Float.valueOf(value);
break;
case "Double":
actualValue = Double.valueOf(value);
break;
case "Boolean":
actualValue = Boolean.valueOf(value);
break;
case "Character":
actualValue = value.charAt(0);
break;
case "String":
actualValue = value;
break;
}
setMethod.invoke(singletonObjects.get(id), actualValue);
}
if (ref != null){
setMethod.invoke(singletonObjects.get(id), singletonObjects.get(ref));
}
}catch (Exception e){
e.printStackTrace();
}
});
}catch (Exception e){
e.printStackTrace();
}
});
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public Object getBean(String beanName) {
return singletonObjects.get(beanName);
}
}
Spring IoC注解式开发
回顾注解
目的:简化配置, spring6倡导全注解开发.
定义 (属性名为value可省略, 数组元素长度为一大括号可省略)
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value();
String name();
String[] names();
int[] ages();
int age();
}
使用
Controller, Service, Repository都是Component的别名, 为了增强可读性
(mvc: 表示层, 业务层, 持久层).
1.加入aop的依赖 (context依赖自动关联aop)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.10</version>
</dependency>
2.配置文件中添加context命名空间
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
3.指定包扫描
<context:component-scan base-package="com.powernode.spring6.bean"/>
4.在bean上使用注解
@Component(value = "userBean")
public class User {
}
注: Component注解如果value没赋值, 默认为类名首字母变小写.
多个包扫描
<context:component-scan base-package="com.powernode.spring6.bean, com.powernode.spring6.dao"/>
选择性实例化bean
方案1
use-default-filters="false" : 让包下所有带有声明bean的注解全部失效.
<context:component-scan base-package="com.powernode.spring6.bean2" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
方案2
use-default-filters="true" : 让包下所有带有声明bean的注解全部生效. (默认值为true).
<context:component-scan base-package="com.powernode.spring6.bean2" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
负责注入的注解
@Value只能注入简单类型
不需要写set方法
@Value("com.mysql.cj.jdbc.Driver")
private String driver;
@Value("jdbc:mysql//localhost:3306/spring6")
private String url;
@Value("root")
private String username;
@Value("1234")
private String password;
@Value用在set方法上
private String name;
private int age;
@Value("隔壁老王")
public void setName(String name) {
this.name = name;
}
@Value("30")
public void setAge(int age) {
this.age = age;
}
@Value用在构造方法上
private String name;
private int age;
public Product(@Value("隔壁老王") String name,@Value("40") int age) {
this.name = name;
this.age = age;
}
注入非简单类型
根据类型装配: @Autowired(默认根据类型装配)
参数上
@Autowired
private OrderDao orderDao;
set方法上
@Autowired
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
构造方法上
@Autowired
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
构造方法参数上
public OrderService(@Autowired OrderDao orderDao) {
this.orderDao = orderDao;
}
根据名字装配: @Autowired与@Qualifier一起用
@Autowired
@Qualifier("orderDaoImplForOracle")
private OrderDao orderDao;
public void generate(){
orderDao.insert();
}
@Resource注解
与@Autowired区别
1.@Resource注解是JDK扩展包中的,也就是说属于JDK的⼀部分。所以该注解是标准注解,更加具有通⽤性。(JSR-250标准中制定的注解类型。JSR是Java规范提案。)
2.@Autowired注解是Spring框架⾃⼰的。
3.@Resource注解默认根据名称装配byName,未指定name时,使⽤属性名作为name。通过name找不到的话会⾃动启动通过类型byType装配。
4.@Autowired注解默认根据类型装配byType,如果想根据名称装配,需要配合@Qualifier注解⼀起⽤。
5.@Resource注解⽤在属性上、setter⽅法上。
6.@Autowired注解⽤在属性上、setter⽅法上、构造⽅法上、构造⽅法参数上。
依赖:
spring6
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
spring5
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
使用
@Resource(name = "studentDaoImplForMySQL")
private StudentDao studentDao;
public void deleteStudent(){
studentDao.deleteById();
}
全注解开发
配置类
@Configuration
@ComponentScan({"cn.powernode.dao", "cn.powernode.service"})
public class Spring6Config {
}
使用
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spring6Config.class);
JdbcTemplate
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
配置
<bean id="ds" class="com.powernode.spring6.bean.MyDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>
使用
增
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into t_user(real_name, age) values(?,?)";
int count = jdbcTemplate.update(sql, "王五", 20);
改
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "update t_user set real_name = ?, age = ? where id = ?";
int count = jdbcTemplate.update(sql, "张三丰", 55, 1);
删
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "delete from t_user where id = ?";
int count = jdbcTemplate.update(sql, 1);
查一个
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select id, real_name, age from t_user where id = ?";
User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), 2);
查所有
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select id, real_name, age from t_user";
List<User> users = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
查一个值
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select count(1) from t_user";
Integer total = jdbcTemplate.queryForObject(sql, int.class);
批量添加
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "insert into t_user(real_name, age) values(?, ?)";
Object[] objs1 = {"小花1", 30};
Object[] objs2 = {"小花2", 31};
Object[] objs3 = {"小花3", 32};
Object[] objs4 = {"小花4", 33};
List<Object[]> list = new ArrayList<>();
list.add(objs1);
list.add(objs2);
list.add(objs3);
list.add(objs4);
int[] count = jdbcTemplate.batchUpdate(sql, list);
批量修改
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "update t_user set real_name = ?, age = ? where id = ?";
Object[] objs1 = {"小明1", 30, 8};
Object[] objs2 = {"小明2", 31, 9};
Object[] objs3 = {"小明3", 32, 10};
Object[] objs4 = {"小明4", 33, 11};
List<Object[]> list = new ArrayList<>();
list.add(objs1);
list.add(objs2);
list.add(objs3);
list.add(objs4);
int[] count = jdbcTemplate.batchUpdate(sql, list);
批量删除
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "delete from t_user where id = ?";
Object[] objs1 = {8};
Object[] objs2 = {9};
Object[] objs3 = {10};
Object[] objs4 = {11};
List<Object[]> list = new ArrayList<>();
list.add(objs1);
list.add(objs2);
list.add(objs3);
list.add(objs4);
int[] count = jdbcTemplate.batchUpdate(sql, list);
回调函数
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
String sql = "select id, real_name,age from t_user where id = ?";
User user = jdbcTemplate.execute(sql, new PreparedStatementCallback<User>() {
@Override
public User doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
User user = null;
ps.setInt(1, 2);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
int id = rs.getInt("id");
String realName = rs.getString("real_name");
int age = rs.getInt("age");
user = new User(id, realName, age);
}
return user;
}
});
使用德鲁伊连接池
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.23</version>
</dependency>
配置
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="1234"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds"/>
</bean>
GoF之代理模式
理解
代理模式中有⼀个⾮常重要的特点:对于客户端程序来说,使⽤代理对象时就像在使⽤⽬标对象⼀
样。
角色:
1.代理类(代理主题)
2.⽬标类(真实主题)
3.代理类和⽬标类的公共接⼝(抽象主题):客户端在使⽤代理类时就像在使⽤⽬标类,不被客户端所察觉,所以代理类和⽬标类要有共同的⾏为,也就是实现共同的接⼝。
分类: 静态代理和动态代理.
静态代理
缺点: 类爆炸(太多, 每个接口都需要一个代理类).
public class OrderServiceProxy implements OrderService{
private OrderService target;
public OrderServiceProxy(OrderService target) {
this.target = target;
}
@Override
public void generate() {
long begin = System.currentTimeMillis();
target.generate();
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - begin) + "ms");
}
@Override
public void modify() {
long begin = System.currentTimeMillis();
target.modify();
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - begin) + "ms");
}
@Override
public void detail() {
long begin = System.currentTimeMillis();
target.detail();
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - begin) + "ms");
}
}
JDK动态代理
在内存当中动态⽣成类的技术常⻅的包括:
1.JDK动态代理技术:只能代理接⼝。
2.CGLIB动态代理技术:CGLIB(Code Generation Library)是⼀个开源项⽬。是⼀个强⼤的,⾼性
能,⾼质量的Code⽣成类库,它可以在运⾏期扩展Java类与实现Java接⼝。它既可以代理接⼝,⼜可以代理类,底层是通过继承的⽅式实现的。性能⽐JDK动态代理要好。(底层有⼀个⼩⽽快的字节码处理框架ASM。)
3.Javassist动态代理技术:Javassist是⼀个开源的分析、编辑和创建Java字节码的类库。是由东京⼯业⼤学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加⼊了开放源代码JBoss 应⽤服务器项⽬,通过使⽤Javassist对字节码操作为JBoss实现动态"AOP"框架。
使用
定义处理器
public class TimerInvocationHandler implements InvocationHandler {
private Object target;
public TimerInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long begin = System.currentTimeMillis();
Object retValue = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - begin) + "ms");
return retValue;
}
}
使用
OrderService target = new OrderServiceImpl();
OrderService proxyObj = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), new TimerInvocationHandler(target));
proxyObj.generate();
proxyObj.detail();
proxyObj.modify();
String name = proxyObj.getName();
CGLIB动态代理
定义回调函数
public class TimerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long begin = System.currentTimeMillis();
Object res = methodProxy.invokeSuper(target, objects);
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - begin) + "ms");
return res;
}
}
使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new TimerMethodInterceptor());
UserService userServiceProxy = (UserService) enhancer.create();
boolean res = userServiceProxy.login("admin", "123");
System.out.println(res);
userServiceProxy.logout();
注:对于⾼版本的JDK,如果使⽤CGLIB,需要在启动项中添加两个启动参数:
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
面向切面编程AOP
理解
⼀般⼀个系统当中都会有⼀些系统服务,例如:⽇志、事务管理、安全等。这些系统服务被称为:交叉业务
⽤⼀句话总结AOP:将与核⼼业务⽆关的代码独⽴的抽取出来,形成⼀个独⽴的组件,然后以横向交叉的⽅式应⽤到业务流程当中的过程被称为AOP。
AOP的优点:
1.代码复⽤性增强。
2.代码易维护。
3.使开发者更关注业务逻辑。
Spring的AOP使⽤的动态代理是:JDK动态代理 + CGLIB动态代理技术。Spring在这两种动态代理中灵活切换,如果是代理接⼝,会默认使⽤JDK动态代理,如果要代理某个类,这个类没有实现接⼝,就会切换使⽤CGLIB。当然,你也可以强制通过⼀些配置让Spring只使⽤CGLIB。
七大术语
连接点 Joinpoint
在程序的整个执⾏流程中,可以织⼊切⾯的位置。⽅法的执⾏前后,异常抛出之后等位置。
切点 Pointcut
在程序执⾏流程中,真正织⼊切⾯的⽅法。(⼀个切点对应多个连接点)
通知 Advice
通知⼜叫增强,就是具体你要织⼊的代码。
通知包括:前置通知, 后置通知, 环绕通知, 异常通知, 最终通知.
切⾯ Aspect
切点 + 通知就是切⾯。
织⼊ Weaving
把通知应⽤到⽬标对象上的过程。
代理对象 Proxy
⼀个⽬标对象被织⼊通知后产⽣的新对象。
⽬标对象 Target
被织⼊通知的对象。
切点表达式
切点表达式⽤来定义通知(Advice)往哪些⽅法上切⼊。
切⼊点表达式语法格式:
execution([访问控制权限修饰符] 返回值类型 [全限定类名]⽅法名(形式参数列表) [异常])
访问控制权限修饰符:
可选项。
没写,就是4个权限都包括。
写public就表示只包括公开的⽅法。
返回值类型:
必填项。
* 表示返回值类型任意。
全限定类名:
可选项。
两个点“..”代表当前包以及⼦包下的所有类。
省略时表示所有的类。
⽅法名:
必填项。
*表示所有⽅法。
set*表示所有的set⽅法。
形式参数列表:
必填项
() 表示没有参数的⽅法
(..) 参数类型和个数随意的⽅法
(*) 只有⼀个参数的⽅法
(*, String) 第⼀个参数类型随意,第⼆个参数是String的。
异常:
可选项。
省略时表示任意异常类型。
使用Spring的AOP
Spring对AOP的实现包括以下3种⽅式:
第⼀种⽅式:Spring框架结合AspectJ框架实现的AOP,基于注解⽅式。
第⼆种⽅式:Spring框架结合AspectJ框架实现的AOP,基于XML⽅式。
第三种⽅式:Spring框架⾃⼰实现的AOP,基于XML配置⽅式。
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.10</version>
</dependency>
命名空间
<?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 http://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 http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
配置
proxy-target-class属性:
true:强制使用CGLIB动态代理.
false:默认值, 接口使用JDK动态代理, 类使用CGLIB动态代理.
<!--开启aspectj的自动代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
切面类
切面类必须加@Aspect注解
@Component
@Aspect
public class LogAspect {
@Before("execution(* com.powernode.spring6.service.UserService.*(..))")
public void 增强(){
System.out.println("我是增强代码...");
}
}
通知类型
前置通知
@Before("execution(* com.powernode.spring6.service..*(..))")
public void beforeAdvice(){
System.out.println("前置通知...");
}
后置通知
@AfterReturning("execution(* com.powernode.spring6.service..*(..))")
public void afterReturningAdvice(){
System.out.println("后置通知...");
}
环绕通知(前置之前, 后置之后)
@Around("execution(* com.powernode.spring6.service..*(..))")
public void aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("前环绕...");
joinPoint.proceed();
System.out.println("后环绕...");
}
最终通知
@After("execution(* com.powernode.spring6.service..*(..))")
public void afterAdvice(){
System.out.println("最终通知...");
}
异常通知(发生异常才会通知)
@AfterThrowing("execution(* com.powernode.spring6.service..*(..))")
public void afterThrowingAdvice(){
System.out.println("异常通知...");
}
不同切面排序
类上加 @Order(整数) 注解, 数字越小, 优先级越高.
切点表达式复用
@Pointcut("execution(* com.powernode.spring6.service..*(..))")
public void commonPoint(){
}
@Before("commonPoint()")
public void beforeAdvice(){
System.out.println("前置通知...");
}
@AfterReturning("commonPoint()")
public void afterReturningAdvice(){
System.out.println("后置通知...");
}
全注解式开发
@Configuration
@ComponentScan("com.powernode.spring6.service")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Spring6Config {
}
配置实现AOP
<?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 http://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 http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.powernode.spring6.service.UserService"/>
<bean id="timerAspect" class="com.powernode.spring6.service.TimerAspect"/>
<aop:config>
<aop:pointcut id="mypointcut" expression="execution(* com.powernode.spring6.service..*(..))"/>
<aop:aspect ref="timerAspect">
<aop:around method="aroundAdvice" pointcut-ref="mypointcut"/>
</aop:aspect>
</aop:config>
</beans>
一个通知有多个切点
@Pointcut("execution(* com.powernode.spring6.biz..save*(..))")
public void savePointcut(){}
@Pointcut("execution(* com.powernode.spring6.biz..modify*(..))")
public void modifyPointcut(){}
@Pointcut("execution(* com.powernode.spring6.biz..delete*(..))")
public void deletePointcut(){}
@Before("savePointcut() || deletePointcut() || modifyPointcut()")
public void beforeAdvice(JoinPoint joinPoint){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String nowTime = sdf.format(new Date());
System.out.println(nowTime + "张三 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
}
Spring对事务的支持
原理: spring对aop的封装.
声明式事务
基于注解
配置
命名空间
<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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
配置事务管理器bean并开启
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
使用
@Transactional (可加在类或方法上)
propagation属性(事务的传播行为)
七种传播行为:
REQUIRED: 没有就新建,有就加⼊.
SUPPORTS:有就加⼊,没有就不管了.
MANDATORY:有就加⼊,没有就抛异常.
REQUIRES_NEW: 不管有没有,直接开启⼀个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起.
NOT_SUPPORTED:不⽀持事务,存在就挂起.
NEVER:不⽀持事务,存在就抛异常.
NESTED:有事务的话,就在这个事务⾥再嵌套⼀个完全独⽴的事务,嵌套的事务可以独⽴的提交
和回滚。没有事务就和REQUIRED⼀样.
事务的四种隔离级别

事务超时
@Transactional(timeout = 5)
注:在当前事务当中,最后⼀条DML语句执⾏之前的时间。如果最后⼀条DML语句后⾯很有很多业务逻辑,这些业务代码执⾏的时间不被计⼊超时时间。
当然,如果想让整个⽅法的所有代码都计⼊超时时间的话,可以在⽅法最后⼀⾏添加⼀⾏⽆关紧要的DML语句。
只读事务
@Transactional(readOnly = true)
将当前事务设置为只读事务,在该事务执⾏过程中只允许select语句执⾏,delete insert update均不可执⾏。
作⽤:启动spring的优化策略。提⾼select语句执⾏效率。
设置哪些异常回滚事务
@Transactional(rollbackFor = RuntimeException.class)
设置哪些异常不回滚事务
@Transactional(noRollbackFor = RuntimeException.class)
事务全注解开发
配置类
@Configuration
@ComponentScan("com.powernode.bank")
@EnableTransactionManagement
public class Spring6Config {
@Bean(name = "dataSource")
public DruidDataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring6");
dataSource.setUsername("root");
dataSource.setPassword("1234");
return dataSource;
}
@Bean(name = "jdbcTemplate")
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean(name = "txManager")
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager tx = new DataSourceTransactionManager();
tx.setDataSource(dataSource);
return tx;
}
}
声明式事务xm实现方式
命名空间
<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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://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 http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
配置切面等
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.powernode.bank.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
Spring6整合JUnit
junit4
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
使用
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class SpringJunit4Test {
@Autowired
private User user;
@Test
public void testUser(){
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
// User user = applicationContext.getBean("user", User.class);
System.out.println(user.getName());
}
}
junit5
配置
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.10</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
使用
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:spring.xml")
public class SpringJunit5Test {
@Autowired
private User user;
@Test
public void testUser(){
System.out.println(user.getName());
}
}
Spring6集成MyBatis5
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
mybatis配置日志 (核心配置文件)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
spring核心配置文件
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--组件扫描-->
<!--<context:component-scan base-package="com.powernode.bank"/>-->
<!--在spring的核心配置文件中引入其他的子spring配置文件-->
<import resource="common.xml"/>
<!--引入外部的属性配置文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--配置SqlSessionFactoryBean-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
<!--指定mybatis核心配置文件-->
<property name="configLocation" value="mybatis-config.xml"/>
<!--指定别名-->
<property name="typeAliasesPackage" value="com.powernode.bank.pojo"/>
</bean>
<!--Mapper扫描配置器,主要扫描Mapper接口,生成代理类-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.powernode.bank.mapper"/>
</bean>
<!--事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--启用事务注解-->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
Spring八大模式
1.简单工厂模式
BeanFactory的getBean()⽅法,通过唯⼀标识来获取Bean对象。是典型的简单⼯⼚模式(静态⼯⼚模式.
2.工厂方法模式
FactoryBean是典型的⼯⼚⽅法模式。在配置⽂件中通过factory-method属性来指定⼯⼚⽅法,该⽅法是⼀个实例⽅法。
3.单例模式
Spring⽤的是双重判断加锁的单例模式.
4.代理模式
Spring的AOP就是使⽤了动态代理实现的。
5.装饰器模式
Spring中类名中带有:Decorator和Wrapper单词的类,都是装饰器模式。
6.观察者模式
定义对象间的⼀对多的关系,当⼀个对象的状态发⽣改变时,所有依赖于它的对象都得到通知并⾃动更新。Spring中观察者模式⼀般⽤在listener的实现.
7.策略模式 (面向接口编程)
策略模式是⾏为性模式,调⽤不同的⽅法,适应⾏为的变化 ,强调⽗类的调⽤⼦类的特性 。
⽐如我们⾃⼰写了AccountDao接⼝,然后这个接⼝下有不同的实现类:AccountDaoForMySQL,
AccountDaoForOracle。对于service来说不需要关⼼底层具体的实现,只需要⾯向AccountDao接⼝调⽤,底层可以灵活切换实现,这就是策略模式。
8.模板方法模式
Spring中的JdbcTemplate类就是⼀个模板类。它就是⼀个模板⽅法设计模式的体现。在模板类的模板⽅法execute中编写核⼼算法,具体的实现步骤在⼦类中完成。


被折叠的 条评论
为什么被折叠?



