运行环境苛刻
代码移植性差
总结:EJB是重量级框架
spring是一个轻量级javaEE解决方案,整合了多种优秀的设计模式
对于运行环境没有额外的要求(开源tomcat,收费weblogic websphere等),代码移植性高
java开发mvc开发中,struts2属于controller,mybatis属于dao,而spring可以解决在javaEE的整个开发过程中每一层的问题(springmvc->controller,aop->service…)
- 工厂
- 代理
- 模板
- 策略
面向对象设计中,解决特定问题的经典代码
耦合:代码间的强关联关系,一方的改变会影响到另一方,不利于代码的维护(理解:接口的实现类硬编码到程序中)
概念:通过工厂类创建对象
好处:解耦合
//对象的创建方式:
// 1.直接调用构造方法创建对象
UserService userService = new UserServiceImpl();
// 2.通过反射的形式创建对象解耦合
Class clazz = Class.forName("***.UserServiceImpl");
UserService userService = (UserService)clazz.newInstance();
通用工厂模式
public class BeanFactory {
private static Properties env = new Properties();
static {
InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
try {
env.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String key) {
Object ret = null;
try {
Class clazz = Class.forName(env.getProperty(key));
ret = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return ret;
}
}
spring本质:工厂 ApplicationContext
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
作用:Spring提供的ApplicationContext这个工厂,用于对象的创建
好处:解耦合
ApplicationContext接口类型
接口:屏蔽实现的差异
非web环境:ClassPathXmlApplicationContext(main,junit)
web环境:XmlWebApplicationContext
ApplicationContext是一个重量级资源
ApplicationContext工厂对象占用内存比较大,不会频繁的创建对象,一个应用只会创建一个工厂对象。
ApplicationContext工厂一定是一个线程安全的(支持多线程并发访问)
Spring工厂创建的对象,叫做bean或者Component
package xyz.hongxiao2020;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.junit.Assert.*;
public class PersonTest {
@Test
public void test1(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println("person = " + person);
}
@Test
public void test() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
//不需要强转
Person person = ctx.getBean("person", Person.class);
System.out.println("person = " + person);
//当前Spring的配置文件中只能有一个bean的class是Person类型
Person person = ctx.getBean(Person.class);
System.out.println("person = " + person);
//获取的是Spring工厂配置文件中所有bean标签的id值
String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName = " + beanDefinitionName);
}
//根据类型获得Spring配置文件中id值
String[] beanNamesForType = ctx.getBeanNamesForType(Person.class);
for (String id : beanNamesForType) {
System.out.println("id = " + id);
}
//用于判断是否存在指定id值得bean,不能判断name值
if (ctx.containsBeanDefinition("person")) {
System.out.println("存在person");
}else{
System.out.println("不存在person");
}
//用于判断是否存在指定id值得bean,也可以判断name值
if (ctx.containsBean("person")) {
System.out.println("存在" );
}else{
System.out.println("不存在");
}
}
}
<bean class="xyz.hongxiao2020.Person"/>
a) 上述这种配置默认的id值为xyz.hongxiao2020.Person#0
b) 应⽤场景: 如果这个bean只需要使用一次,那么就可以省略id值
如果这个bean会使用多次,或者被其他bean引用则需要设置id值
作用:用于在Spring的配置文件中,为bean对象定义别名(小名)
1. ctx.getBean("id|name") -->object
2.
反射的底层调用了对象自己的无参构造方法,即使构造方法是私有的,Spring工厂依然可以调用其构造方法。
问题:在未来的开发过程中,是不是所有的对象都交给Spring工厂来创建那
回答:理论上是的。但是有特例:实体对象(entity)是不会交给spring创建的,它是由持久层框架进行创建的。
Spring与日志框架整合,日志框架就可以在控制台中输出Spring框架运行过程中的一些重要信息
好处:便于了解Spring框架的运行过程,便于调试。
默认:
Spring1.2.3早期都是与commons-logging.jar
Spring5.x默认整合的日志框架:logback log4j2
Spring5.x整合log4j
- 引入log4j jar包
- 引入log4.properties配置文件
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
resources 文件夹根目录下(log4j.properties)
### 配置根
log4j.rootLogger = debug,console
### 日志输出到控制台显示
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
通过Spring工厂及配置文件,为成员变量赋值
注入好处:解耦合
前提是先为成员变量设置set函数,底层是通过set方法来完成属性赋值
针对不同类型的成员变量,在标签中,需要嵌套其他标签
<bean id="person" class="xyz.hongxiao2020.Person">
<property name="age" value="10">property>
<property name="name" value="hongxiao">property>
bean>
直接使用Value赋值
<property name="emails">
<list>
<value>wwwvalue>
<value>cccvalue>
<value>bbbvalue>
list>
property>
<set>
<value>111value>
<value>222value>
<value>333value>
set>
<list>
<value>wwwvalue>
<value>cccvalue>
<value>bbbvalue>
list>
注意: map -- entry -- key有特定的标签 <key>key>
值根据对应类型选择对应类型的标签value或者ref
<map>
<entry>
<key><value>hellovalue>key>
<value>123value>
entry>
...
map>
Properties是一种特殊的Map,他的key是String Value也是String类型
<props>
<prop key="k1">value1prop>
<prop key="k2">value2prop>
props>
第一种方式:
<bean id="userService" class="xxxx.UserServiceImpl">
<property name="userDAO">
<bean class="xxx.UserDAOImpl"/>
property>
bean>
第二种方式:
第一种赋值方式存在的问题:
<bean id="userDAO" class="xxx.UserDAOImpl"/>
<bean id="userService" class="xxx.UserServiceImpl">
<property name="userDAO">
<ref bean="userDAO"/>
property>
bean>
<property name="name">
<value>testvalue>
property>
<property name="name" value="test"/>
<property name="userDAO">
<ref bean="userDAO"/>
property>
property>
利用p标签简化
<bean id="person" class="*.Person" p:name="hong" p:age="23">
通过Spring的配置文件,为成员变量赋值
Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值
构造注入:Spring调用构造方法,通过配置文件,为成员变量赋值
为对象提供有参构造方法
spring配置文件设置
<constructor-arg>
<value>hellovalue>
constructor-arg>
<constructor-arg>
<value>23value>
constructor-arg>
参数不同时:根据控制的数量进行区分
参数相同时:通过在标签引入Type属性进行区分
未来的实战中,应用set注入还是构造注入?
:set注入更多
控制:对于成员变量赋值的控制权
控制反转:把对于成员变量赋值的控制权,从代码中反转(转移)到Spring工厂和配置文件中完成
优点:解耦合
底层实现:工厂模式
注入:通过Spring的工厂及配置文件,为对象(bean,组件)的成员变量赋值
依赖注入:当一个类需要另一个类时,就意味着依赖,一旦出现依赖,就可以把另一个类作为本类的成员变量,最终通过Spring配置文件进行注入(赋值)
复杂对象
简单对象:指的是可以直接通过new(构造方法)创建对象
复杂对象:指的是不可以直接通过new构造方法创建的对象(Connection SqlSessionFactory)
开发步骤:
public class ConnectionFactoryBean implements FactoryBean<Connection> {
//用于书写创建复杂方法的代码,并把复杂对象作为方法的返回值返回
public Connection getObject() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bigData?useSSL=false","root","root");
return conn;
}
//返回所创建复杂对象的Class对象
public Class<?> getObjectType() {
return Connection.class;
}
//是否是单例模式
public boolean isSingleton() {
return false;
}
}
spring配置文件的配置
<bean id="conn" class="*.MyFactoryBean">bean>
细节:
通过注入的方式,解耦
<bean id="conn" class="*.MyFactoryBean">
<property name="className" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/bigData?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
接口回调
- 为什么Spring规定FactoryBean接口实现 并且创建复杂对象的方法写在 getObject()?
- ctx.getBean(“conn”) 获得是复杂对象 Connection,而没有获得ConnectionFactoryBean("&conn")
Spring内部运行流程
1. 通过conn获得ConnectionFactoryBean类的对象 ,进而通过instanceof(FactoryBean) 判断出是FactoryBean接口的实现类
2. Spring按照规定 getObject() --->Connection
3. 返回Connection
总结:
FactoryBean是Spring用于创建复杂类型对象的一种方式,也是Spring原生提供的,Spring在整合其他框架时会大量应用FactoryBean
public class FactoryBean {
public Connection getConnection(){
Connection connection = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql", "root", "root");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
}
<bean id="conn" class="*.FactoryBean">bean>
<bean id="conn2" factory-bean="conn" factory-method="getConnection">bean>
<bean id="conn2" class="*.StaticFactoryBean" factory-method="getConnection">bean>
spring工厂控制简单对象创建次数
默认为单例模式
<bean id="person" class="*.Person" scope="prototype/singleton">bean>
为什么要控制对象的创建次数
最大的好处:节省不必要的内存浪费
什么样的对象只创建一次:sqlSessionFactory Dao Service (共用,线程安全)
什么样的对象需要多次创建:Connection SqlSession | Session
什么是生命周期:一个对象创建、存活、消亡的一个完整过程
为什么要学习对象的生命周期:
由Spring负责对象的创建、存活、销毁,了解生命周期,有利于使用好Spring为我们创建对象
Spring工厂何时创建对象:
scope = singleton Spring在工厂创建的同时,会创建对象
scope = prototype Spring工厂会在获取对象的同时创建对象 (ctx.getBean)
如果想在scope = singleton 模式下设置获取对象的时候创建对象需要设置
lazy-init="true"
Spring工厂会在创建完对象后,调用对象的初始化方法,完成对象相应的初始化操作
方式一:实现InitializingBean
接口实现它的方法
public void afterPropertiesSet() throws Exception {
}
方式二:对象中提供一个普通方法
public void init(){
}
<bean id="prod" class="*.Product" init-method="init"/>
如果一个对象即实现InitializingBean 同时又提供普通的初始化方法顺序
1. InitializingBean
2. 普通初始方法
注入一定发生在初始化之前
什么叫初始化?
资料的初始化:数据库, IO, 网络 ......
Spring销毁对象前,会调用对象的销毁方法,完成销毁操作
ctx.close()
方式一:实现DisposableBean
方式二:定义一个方法,在配置文件中配置destroy-method=""
细节分析:**销毁方法只适用于scope为singleton模式下**
什么叫销毁操作:主要指的是资源的释放操作 io.close() connetion.close()
什么是配置文件参数化:把Spring配置文件中需要经常修改的字符串信息,转移到一个更小的配置文件中,有利于Spring配置文件的维护
1、Spring配置文件中存在需要经常修改的字符串?
存在,以数据库连接相关的参数为代表
2、经常变化的字符串,在Spring配置文件中,直接修改有什么影响
不利于项目的维护
3、转移到小的配置文件中(.properties)
利于维护
名字:随便.properties
放置位置:随便(java文件夹与resource文件夹编译后是放在一起的)
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/bigData?useSSL=false
jdbc.username = root
jdbc.password = root
spring配置文件与小配置文件的整合
<context:property-placeholder location="classpath:/mysql.properties"/>
<bean id="conn" class="xyz.hongxiao2020.ConnectionFactoryBean">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
bean>
作用:Spring通过类型转换器把配置文件中字符串类型的数据,转换成对象成员变量对应的数据类型,进而完成了注入
原因:当Spring内部没有提供特定的类型转换器的时候,而程序员在应用的过程中还需要使用,那么程序员就需要自定义类型转换器
如何自定义一个类型转换器:
//第一个参数是需要被转换的类型,第二个参数是目标参数
public class MyDateConverter implements Converter<String, Date>
在Spring中获取日期字符串:
//直接就可以获取日期字符串
public Date convert(String s) {
Date parse = null;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
parse = format.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
<bean id="convert" class="xyz.hongxiao2020.MyDateConverter"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="convert"/>
set>
property>
bean>
一些细节:
<bean id="convert" class="xyz.hongxiao2020.MyDateConverter">
<property name="pattern" value="yyyy-MM-dd"/>
bean>
ConversionServiceFactoryBean对象的id必须是conversionService
spring提供的默认日期转换器转换的格式为2020/07/22
BeanPostProcessor作用:对Spring工厂所创建的对象再加工。
//程序员需要实现BeanPostProcessor规定接口中的方法
public Object postProcessBeforeInitialization(Object bean, String beanName)
//作用:Spring创建完对象并进行注入,之后运行Before方法进行加工
//获得Spring创建好的对象:通过方法的参数最终通过返回值交给Spring框架
public Object postProcessAfterInitialization(Object bean, String beanName)
//作用:Spring执行完对象初始化操作后,可以运行After方法进行加工
//获得Spring创建好的对象 :通过方法的参数
//最终通过返回值交给Spring框架
//实战中:很少处理Spring的初始化方法:没有必要区分Before和After方法。只需要执行其中一个After方法即可
//注意:使用Before方法需要返回bean对象
// postProcessBeforeInitiallization
// return bean;
两个步骤: 实现BeanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Person){
Person person = (Person) bean;
person.setName("after postProcess...");
}
return bean;
}
}
在Spring配置文件中配置
<bean id="beanPostProcessor" class="xyz.hongxiao2020.MyBeanPostProcessor">bean>
BeanPostProcessor会对Spring工厂中创建的所有对象进行加工,所以我们要判断出需要加工的对象(instance of)
AOP使用了23种设计模式中的静态代理设计模式
为什么要使用代理设计模式?
在JavaEE分层开发中,哪个层次对我们最重要
DAO-Service-Controller 答案是Service 因为里面封装了我们想要实现的业务,用于满足用户需求
Service包含了哪些代码:
Service层中 = 核心功能(几十行 上百行代码)+ 额外功能(附加功能)
1. 核心功能
1. 业务运算
2. Dao调用
2. 额外功能
1. 不属于业务
2. 可有可无
3. 代码量很小
比如(事务,日志,性能)
问题:额外功能写在Service层好不好?
概念:通过代理类,为原始类(目标)增加额外的功能
好处:利于原始类(目标)的维护
代理开发的核心要素:代理类 = 目标类 + 额外功能 + 原始类实现相同的接口
//实现相同的接口
public class UserServiceProxy implements UserService {
//创建原始对象
private UserServiceImpl userService = new UserServiceImpl();
public void register(User user) {
//实现额外功能
System.out.println("---------额外功能----------");
userService.register(user);
}
public boolean login(String name, String password) {
System.out.println("---------额外功能----------");
return userService.login(name,password);
}
}
静态代理:每一个原始类都会手工编写一个代理类
静态代理类文件数目过多,不利于项目管理
额外功能代码维护性差
动态代理相关jar包
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.1.14.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.8.8version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.8.3version>
dependency>
动态代理开发步骤:
创建原始对象(并在配置文件中设置)
额外功能
- MethodBeforeAdvance接口
- 额外的功能写在该接口的实现中,在原始方法执行前运行额外功能
//把运行原始方法执行前的运行的额外功能,书写在before方法中
public class Before implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("MethodBeforeAdvice ......");
}
}
<bean id="before" class="xyz.hongxiao2020.Before"/>
切入点:额外功能加入的位置
目的:程序员根据自己的需求,决定额外功能加入给哪个原始方法
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"/>
aop:config>
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
aop:config>
目的:获得Spring工厂创建的动态代理对象,并进行调用
注意:
Spring的工厂通过原始对象的id值获取的对象为代理对象
获得代理对象后,可以通过声明接口类型,进行对象的存储UserService userService = (UserService) ctx.getBean("userService");
Spring创建的代理类在哪那?
Spring框架在运行时,通过动态字节码技术,在JVM中创建的,在JVM内部,等程序结束后,会和虚拟机一起消失
什么是动态字节码技术:通过第三方动态字节码框架,在虚拟机中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失
结论: 动态代理不需要创建类文件,都是Jvm在运行过程中创建的(没有.java .class文件在本地生产),所以不会造成静态代理类文件数量过多,影响项目管理问题
动态代理模式简化代理的开发:
动态代理额外功能的维护性大大增强
打开扩展,关闭修改
//Method:额外功能所增加给的那个原始方法
//Object[]:额外功能所增加给的那个原始方法的参数
//Object:额外功能所增加给的那个原始对象
public class Before implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("MethodBeforeAdvice ......");
}
}
MethodInterceptor(方法拦截器)
MethodBefore:原始方法执行之前
MethodInterceptor:在方法执行之前和之后都可以使用(实战中使用更多)
/*
invoke方法的作用:额外功能书写在invoke
额外功能 运行情况(3种):1.原始方法之前
2.原始方法之后
3.原始方法之前和之后(事务)
4.抛出异常时
确定:原始方法怎么运行
参数:MethodInvocation:额外功能所增加给的那个原始方法
调用原始方法:invocation.proceed()
返回值:Object: 代表原始方法的返回值
额外功能运行在原始方法执行之前
*/
public class Arround implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("-----额外功能 log----");//额外功能
Object ret = methodInvocation.proceed();
return ret;
}
}
总结:invocation.proceed() 就是原始方法,之前和之后自己可以添加内容
原始方法会抛出异常时:
public Object invoke(MethodInvocation invocation) throws Throwable {
Object ret = null;
try {
ret = invocation.proceed();
} catch (Throwable throwable){
System.out.println("-----原始方法抛出异常 执⾏的额外功能 ----");
throwable.printStackTrace();
}
return ret;
}
切入点决定额外功能的加入位置(方法)
execution() 切入点函数
* *(..) 切入点表达式
//修饰符 返回值 方法名 参数表
public void add (int a, int b)
execution(* *(..))
* --> 修饰符 返回值
* --> 方法名
( ) --> 参数表
.. --> 对于参数没有要求
定义login方法为切入点
execution(* login(..))
只在login方法上切入
# 定义login方法且login方法有两个字符串类型的参数作为切入点
* execution(* login(String, String))
# 注意 对于一些引用变量,非java.lang包中的类型,需要加入全限定类名
* register(xyz.hongxiao2020.demo.User)
# ..可以和具体的参数连用
* login(String, ..)
上面所写的切入点表达式最大的缺点是匹配不精准
精准方法切入点限定:包.类.方法(参数)
*表示包名和类名
<aop:pointcut id="pc" expression="execution(* xyz.hongxiao2020.demo.UserServiceImpl.login(..))"/>
指定类作为额外功能加入的位置
语法一:
<aop:pointcut id="pc" expression="execution(* *.UserServiceImpl.*(..))"/>
语法二:
# 忽略包
1. 类只存在一级包
* *.UserServiceImple.*(..)
2. 类存在多级包(包含一级包)
* *..UserServiceImpl.*(..)
# 指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能
# 注意: 切入点包中所有的类,必须在demo包中,不能在demo包的子包中
* xyz.hongxiao2020.demo.*.*(..)
# 当前包及其子包
* xyz.hongxiao2020.demo..*.*(..)
在实战中更为应用广泛的是包切入点
作用:用于执行切入点表达式
execution
# 最为重要的切入点函数,功能最全
* 执行方法切入点表达式,类切入点表达式,包切入点表达式
弊端:execution 执行切入点表达式,书写麻烦
# 注意:其他切入点函数 是简化execution书写复杂度,功能上完全一致
args
# 作用:主要用于函数(方法参数)的匹配
例如:切入点为方法参数必须是两个字符串类型的参数
execution(* *(String, String))
args(String,String)
within
# 作用:主要用于包、类切入点表达式的匹配
例如:切入点为UserServiceImpl这个类
execution(* *..UserServiceImpl.*(..))
within(*..UserServiceImpl)
execution(* xyz.hongxiao2020.demo..*.*(..))
within(xyz.hongxiao2020.demo..*)
# 其实也就是把execution中的修饰符和方法名简化了,只关注包
@Annotation
作用:为具有特殊注解的方法加入额外功能
//注解所在位置
@Target(ElementType.METHOD)
//注解在什么时候执行 一般都是Runtime
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
<aop:pointcut id="pc" expression="@annotation(xyz.hongxiao2020.demo.Log)"/>
指的是整合多个切入点函数一起配合工作,进而完成更为复杂的需求
案例:login同时参数为两个字符串
execution(* login(String, String))
execution(* login(..)) and args(String, String)
# 注意 与操作不能用于同种类型的切入点函数
案例:register方法和login方法作为切入点
execution(* login(..)) or execution(* register(..))
AOP(Aspect Oriented Programing)面向切面编程 = spring动态代理开发
以切面为基本单位进行程序开发,通过切面间的彼此调用,相互协同,完成程序的构建
切面 = 切入点 + 额外功能
OOP(Object Oriented Programing)面向对象编程
以对象为基本单位进行程序开发,通过对象间的彼此协同,相互协调,完成程序的构建
POP(Producer Oriented Programing)面向过程(方法,函数)编程
以过程为基本单位的程序开发,通过彼此间协同,相互调用,完成程序的构建
AOP:本质上就是 Spring动态代理开发,有利于原始类的维护
# AOP不可能取代OOP,它只是OOP的有意补充
1. 原始对象
2. 额外功能(MethodInterceptor)
3. 切入点
4. 组装切面(额外功能 + 切入点)
切面= 切入点 + 额外功能
几何学:面 = 点 + 相同的功能
AOP如何创建动态代理类(动态字节码技术)
代理创建三要素:1 原始对象 2 额外功能 3 代理对象实现相同的接口
public static void main(String[] args) {
//创建原始对象
UserService userService = new UserServiceImpl();
//JDK创建动态代理
Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
}
public interface InvocationHandler {
//演示原理,没有设置实现类
//作用: 用于书写额外功能 额外功能运行原始方法执行前后 抛出异常
// 参数:proxy 忽略掉,代表的是代理对象
// method 额外功能所增加给的那个原始方法
// Object[] args 原始方法的参数
public Object invoke(Object proxy, Method method, Object[] args){
Object ret = method.invoke(userService,args);
return ret;
}
}
# interfaces:原始对象所实现的接口
userService.getClass().getInterfaces()
类加载器的作用:
如何获得类加载器:每个类的.class文件 自动分配与之对应的ClassLoder
在动态代理创建的过程中,需要ClassLoader创建代理类的Class对象,可是动态代理类没有对应的.class文件,JVM也不会为其分配ClassLoader,但是又需要怎么办?(借用一个ClossLoader)
# ClassLoader:完成代理类的创建
//创建代理类的class对象,进而完成代理类的创建
//注意:类加载器是借用来的(TestJDKProxy或者UserService都行)
public class TestJDKProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------proxy log------");
Object ret = method.invoke(userService, args);
return ret;
}
};
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
userServiceProxy.login("pzh", "1234");
userServiceProxy.register(new User("pzh", "1234"));
}
}
// 对于一些没有实现接口的类
public class UserService {
private UserDao userDao= new UserDaoImpl();
public void register(User user) {
userDao.save(user);
}
public void login(String name, String password) {
userDao.queryGet(name,password);
}
}
//代理类 继承你要代理的类
public clss Proxy extends UserService(){
login(){
//额外功能
super.login();
}
}
public class ProxyCglib {
public static void main(String[] args) {
final UserService userService = new UserService();
/*
通过cglib方式创建动态代理对象
Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
cglib同样也需要做这些:
enhancer.setClassLoader();
enhancer.setSuperclass();
enhancer.setCallback(); -->MethodInterceptor(cglib包下)
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(ProxyCglib.class.getClassLoader());
enhancer.setSuperclass(UserService.class);
MethodInterceptor interceptor = new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("-----cglib log-------");
Object ret = method.invoke(userService, args);
return ret;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("pzh","1234");
userServiceProxy.register(new User("pzh","1234"));
}
}
面试问AOP要把这两个动态代理回答出来
模拟
<bean id="userService" class="xyz.hongxiao2020.factory.UserServiceImpl"/>
<bean id="proxyBeanPostProcessor" class="xyz.hongxiao2020.factory.ProxyBeanPostProcessor"/>
public class ProxyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("new----log");
Object ret = method.invoke(bean, args);
return ret;
}
};
return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), handler);
}
}
<bean id="userService" class="xyz.hongxiao2020.aspect.UserServiceImpl"/>
<bean id="around" class="xyz.hongxiao2020.aspect.MyAspect"/>
<aop:aspectj-autoproxy/>
//告知程序这是一个切面
@Aspect
public class MyAspect {
@Around("execution(* login(..))")
public Object around(ProceedingJoinPoint joinPoint){
Object ret = null;
try {
System.out.println("----aspect log----");
ret = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return ret;
}
}
在切面类中定义一个是函数 上面 @Pointcut()注解 通过这种方式定义切入点表达式,利于复用
@Aspect
public class MyAspect {
@Pointcut("execution(* login(..))")
public void myPointcut(){
}
@Around(value = "myPointcut()")
public Object around(ProceedingJoinPoint joinPoint){
Object ret = null;
try {
System.out.println("----aspect log----");
ret = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return ret;
}
@Around(value = "myPointcut()")
public Object around1(ProceedingJoinPoint joinPoint){
Object ret = null;
try {
System.out.println("----aspect tx----");
ret = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return ret;
}
}
@Pointcut("execution(* *(..))")
public void pointcut(){
}
@Around(value = "pointcut()")
默认情况下 AOP编程底层是基于JDK动态代理的开发方式
注解切换方式
//将proxy-target-class设为true就是基于Cglib的开发模式
<aop:aspectj-autoproxy proxy-target-class="true"/>
传统开发方式切换
<aop:config proxy-target-class="true">
<aop:pointcut id="pc" expression="execution(* *(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="pc"/>
aop:config>
如何在原始类中调用代理类的方法
在同一个业务类中,进行业务间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通方法调用,都调用的是原始方法)。如果想让内层的方法也调用代理对象的方法,就要ApplicationContextAware获得工厂,进而获得代理对象
public void register(User user) {
System.out.println("UserService.register");
//调用的是原始对象的login方法 ---> 核心功能 并不能满足需求
this.login("hello","123");
}
public class UserServiceImpl implements UserService, ApplicationContextAware {
ApplicationContext ctx = null;
public void login(String username, String password) {
System.out.println("UserService.login");
}
/*
设计目的:代理对象的login方法 ---> 额外功能+核心功能
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.login();
Spring工厂重量级资源 一个应用中 应该只创建一个工厂
*/
public void register(User user) {
System.out.println("UserService.register");
//调用的是原始对象的login方法 ---> 核心功能
UserService userService = (UserService) ctx.getBean("userService");
userService.login("hello","123");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ctx = applicationContext;
}
}
AOP知识总结
AOP编程概念(Spring动态代理开发)
概念:通过代理类为原始类增加额外功能
好处:利于原始类的维护
Spring为什么要与持久层进行整合
Spring可以和哪些持久层进行整合
1. JDBC
|- JDBCTemplate
2. Hibernate(JPA)
|- HibernateTemplate
3. Mybatis
|- SqlSessionFactory MapperScannerConfigure
Mybatis开发步骤
实体
public class User implements Serializable {
private Integer id;
private String name;
private String password;
......
实体别名
<typeAliases>
<typeAlias alias="user" type="xyz.hongxiao2020.mybatis.User"/>
typeAliases>
完整配置文件为:
<configuration>
<typeAliases>
<typeAlias alias="user" type="xyz.hongxiao2020.mybatis.User"/>
typeAliases>
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/pzh?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
configuration>
表
create table t_users(id int primary key auto_increment,name varchar(12),password varchar(12));
创建DAO接口
public interface UserDao {
public void save(User user);
}
实现Mapper文件
<mapper namespace="xyz.hongxiao2020.mybatis.UserDao">
<insert id="save" parameterType="user">
insert into t_users(name,password) values (#{name},#{password})
insert>
mapper>
注册Mapper文件
<mappers>
<mapper resource="UserMapper.xml"/>
mappers>
Mybatis API调用
public class TestMybatis {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sessionFactory.openSession();
UserDao userDao = session.getMapper(UserDao.class);
User user = new User();
user.setName("pzh");
user.setPassword("1234");
userDao.save(user);
session.commit();
}
}
Mybatis在开发中存在的问题:配置繁琐,代码冗余
- 实体
# 实体别名 配置繁琐
- 表
- 创建DAO接口
- 实现Mapper文件
# 注册Mapper文件 配置繁琐
# Mybatis API调用 代码冗余
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
# 在Spring中SQLSessionFactoryBean封装了创建SqlSessionFactory的代码
只需要在配置文件中注入SqlSessionFactoryBean对象,就可以省略mybatis-config.xml代码
指定所对应的包名,Spring就会自动创建 User Product
通配的设置 *Mapper.xml --> UserDaoMapper.xml ProductDaoMaper.xml
bean>
在Mybatis-config.xml文件中需要做三件事
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//这里通过配置MapperScannerConfig来设置
设置Dao接口所在的包
最终:创建Dao对象
注意:MapperScannerConfig所创建的DAO对象,他的Id值是接口,单词首字母小写
UserDAO--> userDAO ProductDAO --> ctx.getBean("id")
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.18version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.48version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/pzh?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="xyz.hongxiao2020.entity"/>
<property name="mapperLocations">
<list>
<value>classpath:xyz.hongxiao2020.mapper/*Mapper.xmlvalue>
list>
property>
bean>
<bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<property name="basePackage" value="xyz.hongxiao2020.dao"/>
bean>
编码
实体
package xyz.hongxiao2020.entity;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String name;
private String password;
......
表
数据库创建表
DAO接口
package xyz.hongxiao2020.dao;
import xyz.hongxiao2020.entity.User;
public interface UserDao {
public void save(User user);
}
Mapper文件配置
<mapper namespace="xyz.hongxiao2020.dao.UserDao">
<insert id="save" parameterType="User">
insert into t_users(name,password) values (#{name},#{password})
insert>
mapper>
Spring与Mybatis整合细节
问题:Spring和Mybatis整合后,为什么DAO不提交事务,但是可以插入到数据库中?
本质上控制连接对象由Connection => (连接池)DataSource
1. Mybatis提供连接池对象 --> 创建Connection
Connection.setAutoCommit(false) 手工的控制了事务,操作完成后手工提交
手工的控制了事务,操作完成后手工提交
2. Durid (C3P0 DBCP) 作为连接池 -->创建了Connection
Connection.setAutoCommit(true) 默认自动提交事务,每执行一条sql,提交一次事务
答案:因为Spring和Mybatis整合时,引入了外部连接池对象,保持自动提交事务这个机制,不需要手工提交事务的操作
注意: 在未来的实战中,还会手工控制事务(多条sql一起成功,一起失败),后续通过Spring通过事务控制解决这个问题
什么是事务:保证业务操作完整性的一种数据库机制
事务的四个特点:A C I D
JDBC:
Connection.setAutoCommit(false)
Connection.commit();
Connection.rollback();
Mybatis:
Mybatis自动开启事务
sqlsession(Connection).commit()
sqlsession(Connection).rollback()
# 结论:控制事务的底层,都是通过Connection对象完成的
Spring是通过AOP的方式进行事务开发
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.1.4.RELEASEversion>
dependency>
public class xxxServiceImpl(){
private xxxDAO xxxDAO;
set, get
1. 原始对象-->原始方法-->核心功能(业务处理+DAO调用)
2. Dao作为Service的成员变量,通过依赖注入的方式进行赋值
}
1. MethodInterceptor
public Object invoke(MethodInvocation invocation){
Object ret = null;
try{
Connection.setAutoCommit(false);//开启事务
ret = invocation.proceed()
Connection.commit();//提交事务
}catch(Exception e){
Connection.rollback();//回滚
}
return ret
}
2. @Aspect
@Around
# spring封装了 DateSourceTransactionManager,进行事务管理但是我们需要为其注入连接(注入连接池DateSource)
@Transaction
事务的额外功能加给哪些业务方法
1. 类上:类中所有的方法都会加入事务
2. 方法上:这个方法会加入事务
切入点
额外功能
<tx:annotation-driven transction-manger="dataSourceTransactionManger"/>
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
bean>
<bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="dateSourceTransactionManger" proxy-target-class/>
属性:描述物体特征的一系列值
事务属性:描述事务特征的一系列值
# 隔离属性
# 传播属性
# 只读属性
# 超时属性
# 异常属性
如何添加事务属性
@Transactional(isolation = ,propagation = , readOnly = ,timeout = , rollbackFor = ,noRollbackFor = )
隔离属性:他描述了事务解决并发问题的特征
什么是并发?
多个事务,或者用户,在同一时间访问操作了相同的数据(同一时间:并不是说必须分毫不差,可能差0.00几秒)
并发会产生哪些问题?
并发问题如何解决?
通过隔离属性解决,隔离属性中设置不同的值,解决并发处理过程中的问题
一个事务,读取了另一个事务没有提交的数据,产生数据不一致的问题
解决办法:读已提交 @Transactional(isolation = Isolation.READ_COMMITTED)
一个事务,多次读取相同的数据,但是读取结果不一致,会在本事务中产生数据不一致的问题
注意:
解决方法:@Transactional(isolation = Isolation.REPEATABLE_READ)
本质:一把行锁
一个事务中,多次对整表进行查询统计,但是结果不一样,会在本事务中产生数据不一致的问题。
解决方法:@Transactional(isolation = Isolation.SERIALIZABLE)
本质:表锁
并发安全:SERIALIZABLE>REPEATABLE_READ>READ_COMMITTED
运行效率:READ_COMMITTE>REPEATABLE_READ>SERIALIZABLE
数据库对隔离属性的支持
隔离属性的值 | Mysql | Oracle |
---|---|---|
Isolation.READ_COMMITTED | ✔ | ✔ |
Isolation.REPEATABLE_READ | ✔ | |
Isolation.SERIALIZABLE | ✔ | ✔ |
Oracle不支持REPEATABLE_READ值,如何解决不可重复读
采用多版本比对的方式,解决不可重复读的问题
ISOLATION_DEAFULT :会调用不同数据库所设置的默认隔离属性
Mysql:不可重复读 查询命令: SELECT @@tx_isolation;
Oracle:读已提交
隔离属性在实战中的建议:使用Spring的默认值就好
在未来的实战中,并发访问的几率很低
如果真的遇到并发问题,推荐使用乐观锁
Hibernate(JPA): Version Mybatis:通过拦截器自定义开发
传播属性:描述了事务解决嵌套问题的特征
什么是事务的嵌套:它指的是一个大的事务中,包含若干个小的事务
问题:大事务中融入了很多小的事务,他们彼此影响,最终会导致外部大的事务,丧失了事务的原子性
传播属性的值 | 外部不存在事务 | 外部存在事务 | 用法 | 备注 |
---|---|---|---|---|
REQUIRED | 开启新的事务 | 融入外部事务 | @Transactional(propagation = Propagation.REQUIRED) | 增删改方法 |
SUPPORTS | 不开启事务 | 融入外部事务 | @Transactional(propagation = Propagation.SUPPORTS) | 查询方法 |
REQUIRES_NEW | 开启新的事务 | 挂起外部事务,创建新事务 | @Transactional(propagation = Propagation.REQUIRES_NEW) | 日志记录 |
NOT_SUPPORTED | 不开启事务 | 挂起外部事物 | @Transactional(propagation = Propagation.NOT_SUPPORTED) | 极其不常用 |
NEVER | 不开启事务 | 抛出异常 | @Transactional(propagation = Propagation.NEVER) | 极其不常用 |
MANDATORY | 抛出异常 | 融合到外部事务中 | @Transactional(propagation = Propagation.MANDATORY) | 极其不常用 |
Required
是传播属性的默认值(增删改)
查询:显示指定传播属性为Supports
针对于只进行查询操作的业务方法,可以加入只读属性,提高运行效率
默认值为false
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
public void login(String name, String password) {
}
指定了事务等待的最长时间(单位:秒(s))
为什么事务要进行等待?
当前事务访问数据时,有可能访问的数据被其他事务加锁处理,那么此时本事务就必须进行等待
应用:@Transactional(timeout = 2)
超时抛出异常
超时属性默认值为-1,最终由数据库指定(在开发中很少使用)
Spring事务处理过程中
默认 对RuntimeException及其子类,采用的是回滚的机制
默认 对Exception及其子类,采用的是提交事务的机制
rollbackFor = {java.lang.Exception} 开启回滚
noRollbackFor = {java.lang.RuntimeEexception} 关闭回滚
建议:实战中使用默认就可以了
总结:
1. 隔离属性 默认值
2. 传播属性 Required(默认值) 增删改 Supports 查询
3. 只读属性 默认值(false) 增删改 true 查询
4. 超时属性 默认值
5. 异常属性 默认值
增删改:@Transactional
查询:@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)
标签式事务配置
基于注解回顾:
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
bean>
<bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
@Transactional
public class UserServiceImpl implements UserService{
private UserDao userDao;
......
<tx:annotation-driven transaction-manager="dateSourceTransactionManger" proxy-target-class="true"/>
基于标签
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
bean>
<bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="dateSourceTransactionManger">
<tx:attributes>
<tx:method name="register" isolation="DEFAULT" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* xyz.hongxiao2020.service.UserServiceImpl.register(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc">aop:advisor>
aop:config>
实战应用
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
bean>
<bean id="dateSourceTransactionManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="dateSourceTransactionManger">
<tx:attributes>
<tx:method name="register"/>
<tx:method name="modify*">tx:method>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* xyz.hongxiao2020.service..*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc">aop:advisor>
aop:config>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>javax.servlet.jsp-apiartifactId>
<version>2.3.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.8.8version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.8.3version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.48version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.18version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
1. MVC框架提供了控制器(Controller)调用Service
dao-> service->controller
2. 请求响应的处理
3. 接收请求参数
4. 控制程序的运行流程
5. 试图解析 (JSP JSON Freemarker Thyemeleaf)
Spring可以整合那些MVC框架
1. struts1
2. webwork
3. jsf
4. struts2
5. springMVC
Spring整合MVC框架的核心思想
# 1. Web开发过程中如何创建工厂
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
WebXmlApplicationContext()
# 2.如何保证工厂唯一且被共用
被共用:Web request|session|ServletContext(application)
工厂存储在ServletContext这个作用域中,ServletContext.setAttritube("xxxx",ctx);
唯一:ServletContext对象创建的同时执行new ClassPathXmlApplicationContext("/applicationContext.xml");
ServletContextlistener在ServletContext对象创建的同时,被调用(只会被调用一次),把工厂创建的代码写在ServletContextListener中,也会保证只调用一次,最终工厂就保证了唯一性
# 3.总结
在开发和创建工厂的过程中,既要创建工厂同时又要保证工厂的唯一和被大家所共用。
ServletContextListener(唯一)
ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
ServletContext.setAttritube("xxxx",ctx);
# 4.spring中封装了ContextLoaderListener
1. 创建工厂
2. 把工厂存储在ServletContext中
ContextLoaderListener使用方式
web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
配置文件位置
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
依赖注入: 把Service对象注入给控制器对象
Spring与Struts2整合思路分析
1. Struts2中的Action需要通过Spring的以来注入获得Service对象
Spring与Struts2整合的编码实现
搭建开发环境
引入相关jar(Spring Struts2)
<dependency>
<groupId>org.apache.strutsgroupId>
<artifactId>struts2-spring-pluginartifactId>
<version>2.3.8version>
dependency>
引入对应的配置文件
applicationContext.xml
struts.xml
log4j.properties
初始化配置
Spring(ContextLoaderListener -> web.xml)
Structs2(Filter -> web.xml)
<web-app>
<display-name>Archetype Created Web Applicationdisplay-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext-*.xmlparam-value>
context-param>
<filter>
<filter-name>struts2filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
filter>
<filter-mapping>
<filter-name>struts2filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
web-app>
- 编码
- 开发service对象
最终在spring配置文件中创建service对象
<bean id="userService" class="xyz.hongxiao2020.struts2.UserServiceImpl"/>
- 开发Action对象
- 开发类
import com.opensymphony.xwork2.Action;
public class RegAction implements Action {
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public String execute() throws Exception {
userService.register();
return Action.SUCCESS;
}
}
- Spring(applicationContext.xml)
<bean id="userService" class="xyz.hongxiao2020.struts2.UserServiceImpl"/>
<bean id="regAction" class="xyz.hongxiao2020.struts2.RegAction" scope="prototype">
<property name="userService" ref="userService"/>
bean>
- Struts2(struts.xml)
<struts>
<package name="ssm" extends="struts-default">
url reg.action --->接收到用户的请求后,创建RegAction对象,进行相应的处理
<action name="reg" class="regAction">
<result name="success">/index.jspresult>
action>
package>
struts>
思路分析
SSM = Spring+Struts Spring+Mybatis
整合编码
搭建开发
- 引入相关jar(Spring Struts2 Mybatis)
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>javax.servlet.jsp-apiartifactId>
<version>2.3.1version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjrtartifactId>
<version>1.8.8version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.8.3version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.1.4.RELEASEversion>
dependency>
<dependency>
<groupId>org.apache.strutsgroupId>
<artifactId>struts2-spring-pluginartifactId>
<version>2.3.8version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.48version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.6version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.18version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
applicationContext.xml
struts.xml
log4j.properties
mybatis-config.xml xxxMapper.xml
初始化配置
Spring(ContextLoaderListener -> web.xml)
Struts(Filter -> web.xml)
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<filter>
<filter-name>struts2filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterfilter-class>
filter>
<filter-mapping>
<filter-name>struts2filter-name>
<url-pattern>/*url-pattern>
filter-mapping>
编码
DAO
1. 配置文件的配置
1. DataSource
2. SqlSessionFactory ---> SqlSessionFactoryBean
1. dataSource
2.typeAliasesPackage
3. mapperLocations
3. MapperScannerCobfigur --->DAO接口实现
2. 编码
1. entity
2. table
3. DAO接口
4. 实现Mapper文件
Service (Spring添加事务)
1. 原始对象 ---> 注入DAO
2. 额外功能 ----> DataSourceTransactionMapper --->dataSource
3. 切入点 + 事务属性 @Transactional()
4. 组装切面
Controller (Spring+struts2)
1. 开发控制器 implements Action 注入Service
2. Spring配置文件
1. 注入service
2. scope="prototype"
3. struts.xml
完整代码
reg.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Title
regOk.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
regOk
struts.xml
<struts>
<package name="ssm" extends="struts-default">
<action name="reg" class="regAction">
<result name="success">/regOk.jspresult>
action>
package>
struts>
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/pzh?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="xyz.hongxiao2020.entity"/>
<property name="mapperLocations">
<list>
<value>classpath:xyz.hongxiao2020.mapper/*Mapper.xmlvalue>
list>
property>
bean>
<bean id="configure" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<property name="basePackage" value="xyz.hongxiao2020.dao"/>
bean>
<bean id="userService" class="xyz.hongxiao2020.service.UserServiceImpl">
<property name="userDao" ref="userDao">property>
bean>
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<bean id="regAction" class="xyz.hongxiao2020.action.RegAction" scope="prototype">
<property name="userService" ref="userService"/>
bean>
beans>
spring开发过程中多配置文件的处理
spring会根据需要,把配置信息分门别类的放置在多个配置文件中,便于后续的管理及维护
DAO ---> applicationContext-dao.xml
Service ---> applicationContext-service.xml
Action ---> applicationContext-action.xml
注意:虽然提供了多个配置文件,但是后续应用过程中,还要进行整合
通配符方式
1. 非web环境
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-*.xml");
2. web环境
contextConfigLocation
classpath:applicationContext-*.xml
applicationContext.xml 目的整合其他配置内容
<import resource="applicationContext-dao.xml"/>
<import resource="applicationContext-service.xml"/>
<import resource="applicationContext-action.xml"/>
@Component
public class XX()
为什么要使用注解编程
1. 注解开发方便
代码简洁 开发速度大大提高
2. Spring开发潮流
Spring2.x引入注解,spring3.x完善注解 SpringBoot普及推广注解编程
注解的作用
替换XML这种配置形式,简化配置
@Component
public class User{
}
代替了
替换接口,实现调用双方的契约性
通过注解的方式,在功能调用者和功能提供者之间达成约定,进而进行功能的调用。因为注解应用更为方便灵活,所以在现在的开发中,更推荐通过注解的形式来完成。
spring注解的发展历程
1. Spring2.x开始支持注解编程 @Component @Service @Scope
目的:提供这些注解只是为了在某些情况下简化XML的配置,作为XML开发的有益补充。
2. Spring3.x @Configuration @Bean...
目的:彻底替换xml,基于纯注解编程
3. Spring4.x SpringBoot
提倡使用注解常见开发
Spring注解开发的一个问题
Spring基于注解进行配置后,还能否解耦合呢?
在Spring框架应用注解时,如果对注解配置的内容不满意,可以通过Spring的配置文件进行覆盖的。
这个阶段的注解,仅仅是简化xml的配置,并不能完全替代xml
搭建开发环境
作用:让spring框架在设置的包及其子包中扫描对应的注解,使其生效
对象创建相关注解
@Component
作用:替换原有Spring配置文件中的
Component细节
如何显示的指定工厂创建对象的id值
@Component("u")
Spring配置文件覆盖注解配置内容
applicationContext.xml
<bean id="u" class="xyz.hongxiao2020.bean.User">
<property name="id" value="10"/>
bean>
id class 要和注解中的设置保持一致
Component衍生注解
@Repository --->XXXDAO
@Service --->XXXService
@Controller --->XXXAction
注意:本质上这些衍生注解就是@Component 作用 细节 用法都是完全一样的
目的:更准确表达一个类型的作用
注意:Spring整合Mybatis的开发过程中,不使用@Repository @Component
@Scope注解
作用:控制简单对象的创建次数
注意:不添加@Scope注解 Spring默认值是singleton
@Lazy注解
作用:延迟创建单实例对象
注意:一旦使用了@Lazy注解后,Spring会在使用这个对象的时候,进行这个对象的创建
生命周期方法相关注解
1. 初始化相关方法 @PostConstruct
InitializingBean
2. 销毁方法 @PreDestroy
DisposableBean
@PostConstruct
public void myInit(){
System.out.println("User.myInit");
}
@PreDestroy
public void myDestroy(){
System.out.println("User.myDestroy");
}
注意: 1. 上述的两个注解并不是Spring提供的,JSR(JavaEE规范)520
2. 再一次的验证,通过注解实现了接口的契约性
用户自定义类型 @Autowired
@Service
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
@Autowired
public void setUserDAO(UserDAO userDAO) {
System.out.println("UserServiceImpl.setUserDAO");
this.userDAO = userDAO;
}
public void register(User user) {
userDAO.save(user);
}
}
@Repository
public class UserDAOImpl implements UserDAO {
private User user;
@Autowired
public void setUser(User user) {
this.user = user;
}
public void save(User user) {
System.out.println("UserDAOImpl.save");
}
}
@Autowired细节
1. Autowired注解是基于类型进行注入 [推荐]
基于类型的注入:注入对象的类型,必须与目标成员变量类型相同或者是其子类(实现类
2. Autowired Qualifier 基于名字进行注入
基于名字的注入:注入对象的id值,必须与Qualifier注解中设置的名字相同
3. Autowired注解放置位置
a)放置在对应成员变量的set方法上
b)直接把这个注解放置在成员变量上,spring直接通过反射对成员变量进行注入(赋值)(没有调用set方法)[推荐]
4. JavaEE规范中类似功能的注解
JSR250 @Resource(name="") 基于名字进行注入
注意:如果在应用Resource注解时,名字没有配对成功,那么它会继续按照类型进行注入
JSR330 @Inject 作用和@Autowired完全一致 基于类型进行注入 --->EJB3.0
javax.inject
javax.inject
1
@Service
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
@Autowired
@Qualifier("userDAOImpl2")
public void setUserDAO(UserDAO userDAO) {
System.out.println("UserServiceImpl.setUserDAO");
this.userDAO = userDAO;
}
public void register(User user) {
userDAO.save(user);
}
}
@Repository
public class UserDAOImpl implements UserDAO {
private User user;
@Autowired
public void setUser(User user) {
this.user = user;
}
public void save(User user) {
System.out.println("UserDAOImpl.save");
}
}
@Repository
public class UserDAOImpl2 implements UserDAO {
private User user;
@Autowired
public void setUser(User user) {
this.user = user;
}
public void save(User user) {
System.out.println("UserDAOImpl2.save");
}
}
JDK类型
@Value注解完成
1. 设置xxx.properties
id = 9
name = pzh
password = 123
2. Spring工厂读取配置文件
3. 代码
属性 @Value("${id}")
public class User implements Serializable {
@Value("${id}")
private Integer id;
@Value("${name}")
private String name;
@Value("${password}")
private String password;
1. 作用:用于替换配置文件中的 标签
2. 开发步骤
1. 设置xxx.properties
2. 应用@PropertySource
@Component
@Lazy
@PropertySource("xxx.properties")
public class User implements Serializable {
@Value("${id}")
private Integer id;
@Value("${name}")
private String name;
@Value("${password}")
private String password;
......
3. 代码 @Value("${}")
@Value注解使用细节
@Value注解不能应用在静态成员变量上
如果应用赋值和注入将失败
@Value注解+properties这种方法不能注入集合类型
Spring提供新的配置形式 YAML YML (SpringBoot)
当前包 及其子包
排除方式
1. assignable:排除特定的类型
2. annotation:排除特定的注解
3. aspectj:排除包切入点或者类切入点表达式
4. regex:排除正则表达式
5. custom:自定义排除策略,框架底层开发
注意:排除策略可以叠加使用
包含方式
1. use-default-filters="false"
作用:让spring默认的注解扫描方式失效
2.
作用:指定需要扫描的路径
type作用方式一样
注意:包含也支持叠加策略
配置互通
Spring注解配置 配置文件的配置 互通
@Repository
public class UserDaoImpl{
}
public class UserServiceImpl{
private UserDao userDao;
set;
}
什么情况下使用注解 什么情况下使用配置文件
@Component 替换
基础注解(@Component @Autowired @Value) 程序员开发类型的配置
1. 在程序员开发的类型上 可以加入对应的注解 进行对象的创建
2. 应用其他非程序员开发的类型时,还是需要使用 标签进行配置的(SqlSessionFactoryBean MapperScannerConfigurer)
引入相关jar [SSM pom]
引入相关配置文
初始化配置
编码
<context:component-scan base-package="xyz.hongxiao2020"/>
DAO(Spring+Mybatis)
1. 配置文件的配置
1. DataSource
2. SqlSessionFactory ---> SqlSessionFactoryBean
1. dataSource
2.typeAliasesPackage
3. mapperLocations
3. MapperScannerConfigur --->DAO接口实现
2. 编码
1. entity
2. table
3. DAO接口
4. 实现Mapper文件
Service
1. 原始对象 ---> 注入DAO
@Service --->@Autowired
2. 额外功能 ----> DataSourceTransactionMapper --->dataSource
3. 切入点 + 事务属性 @Transactional()
4. 组装切面
Controller(Spring+struts2)
1. @Controller
@Scope("prototype")
public class RegAction implements Action {
private User user;
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@Override
public String execute() throws Exception {
userService.register(user);
return Action.SUCCESS;
}
}
2. struts.xml
Spring在3.x提供的新的注解,用于替换xml配置文件
@Configuration
public class AppConfig{
}
1. @Configuration的出现时为了替换原来的application.xml等文件
2. AnnotationConfigApplicationContext
1. 创建工厂代码
ApplicationContext ctx = new AnnotationConfigApplicationContext();
2. 指定配置文件
1. 指定配置bean的Class
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
2. 指定配置bean所在的路径
ApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020");
配置Bean开发的细节分析
基于注解开发使用日志
1. 不能集成Log4j
2. 集成logback
引入相关jar包
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>jcl-over-slf4jartifactId>
<version>1.7.25version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>org.logback-extensionsgroupId>
<artifactId>logback-ext-springartifactId>
<version>0.1.4version>
dependency>
引入logback配置文件
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
encoder>
appender>
<root>
<level value="DEBUG" />
<appender-ref ref="STDOUT" />
root>
configuration>
@Configuration注解的本质
本质:也是@Component注解的衍生注解
可以应用
@Bean注解在配置bean中进行使用,等同于XML配置文件中的标签
对象的创建
1. 简单对象
直接通过new方式创建的对象
@Configuration
public class AppConfig {
@Bean
public User user(){
return new User();
}
}
方法名对应id
2. 复杂对象
不能通过new的方式直接创建的对象
@Configuration
public class AppConfig {
@Bean
public Connection conn(){
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost/pzh?useSSL=false", "root", "root");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn;
}
}
与FactoryBean集成
public class ConnectionFactoryBean implements FactoryBean {
public Connection getObject() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/pzh?useSSL=false", "root", "root");
return conn;
}
public Class> getObjectType() {
return Connection.class;
}
public boolean isSingleton() {
return false;
}
}
@Configuration
public class AppConfig {
@Bean
public Connection conn(){
Connection connection = null;
try {
ConnectionFactoryBean factoryBean = new ConnectionFactoryBean();
connection = factoryBean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
}
自定义对象id值
@Bean("id")
控制对象创建次数
@Configuration
public class AppConfig {
@Bean("u")
@Scope("prototype")
public User user (){
return new User();
}
}
用户自定义类型
//参数注入
@Configuration
public class AppConfig {
@Bean
public UserDAO userDAO(){
return new UserDAOImpl();
}
@Bean
public UserService userService(UserDAO userDAO){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
//简化写法
@Configuration
public class AppConfig {
@Bean
public UserDAO userDAO(){
return new UserDAOImpl();
}
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO());
return userService;
}
}
JDK类型的注入
@Bean
public User user(){
User user = new User();
user.setId(1);
user.setName("pzh");
return user;
}
JDK类型注入的细节分析
//代码中进行set方法的调用存在耦合
@Configuration
@PropertySource("classpath:/init.properties")
public class AppConfig {
@Value("${id}")
private Integer id;
@Value("${name}")
private String name;
@Bean
public User user(){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
}
@ComponentScan注解在配置bean中使用,等同于xml配置文件中的标签
目的:进行相关注解的扫描 (@Component @Value .. @Autowired)
基本使用
@Configuration
@ComponentScan(basePackages = "xyz.hongxiao2020.scan")
public class AppConfig {
...
}
<context:component-scan base-package="">
排除、包含的使用
排除
@Configuration
@ComponentScan(basePackages = "xyz.hongxiao2020.scan",
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Service.class),
@ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = {
"xyz.hongxiao2020..*"})})
public class AppConfig1 {
}
type=FilterType.ANNOTATION value
ASSIGNABLE_TYPE value
ASPECTJ pattern
REGEX pattern
CUSTOM value
- 包含
```java
@Configuration
@ComponentScan(basePackages = "xyz.hongxiao2020.scan",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Service.class})})
public class AppConfig1 {
}
多种配置方式的应用场景
1. @Component 衍生 @Autowired 程序员自己开发的类型上 UserService UserDAO Controller...
2. @Bean 框架提供的类型,别的程序员开发的类型(没有源码)SqlSessionFactoryBean MapperScannerConfigure
3. 纯注解的开发过程中,基本不用,遗留系统的整合
4. @Import注解 @Configuration
@Import(User.class) --->得到对象 xyz.hongxiao2020.entity.User
@Import 1.Spring框架的底层使用
2.多配置bean整合
配置的优先级
1. @Component及其衍生注解 < @Bean < 配置文件bean标签
2. 优先级高的配置 覆盖优先级低的配置 (id值保持一致)配置文件与注解整合@ImportResource("applicationContext.xml")
解决基于注解进行配置的耦合问题
优先级覆盖 xml覆盖注解(相同id)
@Configuration
@ComponentScan(basePackages = "xyz.hongxiao2020")
public class AppConfig {
@Bean
public UserDAO userDAO(){
return new UserDAOImpl();
}
}
@Configuration
@ComponentScan(basePackages = "xyz.hongxiao2020")
@ImportResource("applicationContext.xml")
public class AppConfigNew {
@Bean
public UserDAO userDAO(){
return new UserDAOImpl();
}
}
//applicationContext.xml
//
// AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class,AppConfigNew.class);
// AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020");
为什么会有多个配置信息
拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想
多配置信息整合的方式
整合多种配置需要关注那些要点
多配置的信息汇总
base-package进行多个配置Bean的整合
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020.config");
@Import
1. 可以创建对象
2. 多配置bena的整合
@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {
@Bean
public User user(){
return new User();
}
}
在工厂创建时指定多个配置Bean的class对象
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class, AppConfig2.class);
跨配置进行注入
//在应用配置Bean的过程中,不管使用哪种方式进行配置信息的汇总,其操作方式都是通过成员变量加入@Autowired注解完成的
@Configuration
public class AppConfig2 {
@Bean
public UserDAO userDAO(){
return new UserDAOImpl();
}
}
@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
配置Bean与@Component相关注解的整合
@Configuration
@ComponentScan(basePackages = "xyz.hongxiao2020.dao")
public class AppConfig1 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
配置Bean与配置文件整合
1. 遗留系统的整合
2. 配置覆盖
@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig1 {
@Autowired
private UserDAO userDAO;
@Bean
public UserService userService(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDAO(userDAO);
return userService;
}
}
Spring在配置Bean中加入@Configuration注解后,底层就会通过Cglib的代理方式,进行对象相关的配置、处理
什么是四维一体
Spring开发一个功能的四种形式,虽然开发方式不同,但最终效果是一样的
1. 基于schema
2. 基于特定功能注解
3. 基于原始
4. 基于@Bean注解
四维一体的开发案例
1.
2. @PropertySource("four.properties") 【推荐】
3.
4. @Bean
public PropertySourcesPlaceholderConfigurer configurer(){
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("four.properties"));
return configurer;
} 【推荐】
搭建环境
1. 应用配置Bean
2. 注解扫描
开发步骤
1. 原始对象
@Service
public class UserServiceImpl implements UserService {
public void register() {
System.out.println("UserServiceImpl.register");
}
public void login() {
System.out.println("UserServiceImpl.login");
}
}
2. 创建切面类 (额外功能 切入点 组装切面)
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* xyz.hongxiao2020.aop..*.*(..))")
public void pointCut(){
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("-----log------");
Object proceed = joinPoint.proceed();
return proceed;
}
}
3. Spring的配置文件中
替换为
@EnableAspectJAutoProxy --->配置Bean
@Configuration
@ComponentScan(basePackages = {"xyz.hongxiao2020.aop"})
@EnableAspectJAutoProxy
public class AppConfig {
}
注解AOP细节分析
1. 代理创建方式的切换 JDK Cglib
@EnableAspectJAutoProxy(proxyTargetClass)
2. SpringBoot AOP开发方式
@EnableAspectJAutoProxy 已经设置好了
1. 原始对象
@Service
public class UserServiceImpl implements UserService {
public void register() {
System.out.println("UserServiceImpl.register");
}
public void login() {
System.out.println("UserServiceImpl.login");
}
}
2. 创建切面类 (额外功能 切入点 组装切面)
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* xyz.hongxiao2020.aop..*.*(..))")
public void pointCut(){
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("-----log------");
Object proceed = joinPoint.proceed();
return proceed;
}
}
Spring AOP 代理默认实现 JDK
SpringBoot AOP 代理默认实现 Cglib
基础配置
1. 连接池
2. SqlSessionFactory
3. MapperScannerConfigure
编码
@Configuration
@ComponentScan(basePackages = {
"xyz.hongxiao2020.mybatis"})
@MapperScan(basePackages = {
"xyz.hongxiao2020.mybatis"})
public class MybatisAutoConfiguration {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/pzh?useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setTypeAliasesPackage("xyz.hongxiao2020.mybatis");
sessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
return sessionFactoryBean;
}
}
MapperLocations编码时通配的写法
//设置Mapper文件路径
sessionFactoryBean.setMapperLocations(Resource..);
Resource resource = new ClassPathResource("UserDAOMapper.xml");
sessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
/*
classpath:xyz.hongxiao2020.mybatis.mapper/*Mapper.xml
*/
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("xyz.hongxiao2020.mybatis.mapper/*Mapper.xml");
sessionFactoryBean.setMapperLocations(resources);
配置Bean数据耦合的问题
mybatis.properties
mybatis.driverClassName = com.mysql.jdbc.Driver
mybatis.url = jdbc:mysql://localhost/pzh?useSSL=false
mybatis.username = root
mybatis.password = root
mybatis.typeAliasesPackages = xyz.hongxiao2020.mybatis
mybatis.mapperLocations = xyz.hongxiao2020.mybatis.mapper/*Mapper.xml
@Component
@PropertySource("classpath:/mybatis.properties")
public class MybatisProperties {
@Value("${mybatis.driverClassName}")
private String driverClassName;
@Value("${mybatis.url}")
private String url;
@Value("${mybatis.username}")
private String username;
@Value("${mybatis.password}")
private String password;
@Value("${mybatis.typeAliasesPackages}")
private String typeAliasesPackages;
@Value("${mybatis.mapperLocations}")
private String mapperLocations;
public String getDriverClassName() {
return driverClassName;
}
......
@Configuration
@ComponentScan(basePackages = {
"xyz.hongxiao2020.mybatis"})
@MapperScan(basePackages = {
"xyz.hongxiao2020.mybatis"})
public class MybatisAutoConfiguration {
@Autowired
private MybatisProperties mybatisProperties;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(mybatisProperties.getDriverClassName());
dataSource.setUrl(mybatisProperties.getUrl());
dataSource.setUsername(mybatisProperties.getUsername());
dataSource.setPassword(mybatisProperties.getPassword());
return dataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackages());
// sessionFactoryBean.setMapperLocations(new ClassPathResource("UserDAOMapper.xml"));
try {
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(mybatisProperties.getMapperLocations());
sessionFactoryBean.setMapperLocations(resources);
} catch (IOException e) {
e.printStackTrace();
}
return sessionFactoryBean;
}
}
1. 原始对象
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
2. 额外功能
@Configuration
@EnableTransactionManagement
public class TransactionAutoConfiguration {
@Autowired
DataSource dataSource;
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
3. 事务属性
@Transactional
public class UserServiceImpl implements UserService{
private UserDAO userDAO;
}
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO userDAO;
public UserDAO getUserDAO() {
return userDAO;
}
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
4. 基于Schema的事务配置
@Configuration
@EnableTransactionManagement
public class TransactionAutoConfiguration {
@Autowired
DataSource dataSource;
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
1. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("xyz.hongxiao2020.mybatis");
SpringBoot 实现思想
2. SpringMybatis --->DAO 事务基于注解 --> Service Controller
org.springframework.web.context.ContextLoaderListener 只能使用xml创建工厂 无法提供 AnnotationConfigApplicationContext
什么是YML
YML(YAML)是一种新形式的配置文件,比XML更简单,比Properties更强大
Properties进行配置的问题
1. Properties表达过于繁琐,无法表达数据的内在联系
2. Properties无法表达对象、集合类型
YML语法简介
1. 定义yml文件
xxx.yml xxx.yaml
2. 语法
1. 普通语法
name:value
2. 对象概念(空格)
account:
id:1
name:222
3. 定义集合
service:
- 1111
- 2222
Spring与YML集成思路的分析
1. 准备配置文件
init.yml
name:root
password:root
2. 读取yml 转换成 Properties
YamlPropertiesFactoryBean.setResources(yml配置文件路径) new ClassPathResource();
YamlPropertiesFactoryBean.getObject() ---> Properties
3. 应用PropertySourcePlaceholderConfigurer
PropertySourcePlaceholderConfigurer.setProperties()
4. 类中 @Value注解 注入
Spring与YML集成编码
环境搭建
<dependency>
<groupId>org.yamlgroupId>
<artifactId>snakeyamlartifactId>
<version>1.23version>
dependency>
最低版本 1.18
编码
1. 准备yml配置文件
2. 配置Bean中操作 完成YAML读取 与 PropertySourcePlaceholderConfigure的创建
@Bean
public PropertySourcesPlaceholderConfigurer configurer(){
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(new ClassPathResource("init.yml"));
Properties properties = yamlPropertiesFactoryBean.getObject();
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setProperties(properties);
return configurer;
}
3. 类 加入 @Value注解
Spring与YML集成的问题
1. 集合处理问题
SpringEL表达式解决
@Value("#{'${list}'.split(',')}")
2. 对象类型的YML进行配置时 过于繁琐
@Value("${account.name}")
SpringBoot @ConfigurationProperties