Spring相关知识点整理
- Spring体系结构
- Spring程序开发步骤
- Spring配置文件
- Bean标签的基本配置
- Bean标签的范围配置
- 默认情况下演示:
- ` ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");`
- 内部Bean----匿名,外部无法访问,无别名
- util名称空间---创建集合的id,方便引用
- 级联属性----选择属性的属性
- ref引用是地址引用,可以理解为c 里面的地址传递
- 继承配置信息
- abstract---当前bean只能被其他bean继承相关配置数据,而无法创建实例化的bean对象,不用写全类名
- IOC容器创建时候,容器中的所有Bean对象也会随之创建,并且Bean标签就等同于new 一个Bean对象,会在创建的Bean对象的时候,调用其无参构造。
- Bean的创建顺序是按照xml中配置顺序创建的
- Spring 给Bean属性注入null值
- 在容器中注册一个组件(Bean标签就是组件的注册,等同于new一个Bean对象)时,同一个组件(对象)默认是单例的,容器启动完成,容器中所有组件创建完毕
- IOC容器在创建组件对象的时候,如果使用Property标签对属性进行赋值,那么默认利用对象的setter属性进行赋值
- javaBean对象的属性名由对象的setter方法,去掉set后面,后面的那一串首字母小写就是属性名
- getBean方法的三个重置版本
- 单例情况下演示:
- 多例演示:
- 注意
- Bean(对象)的生命周期配置
- 单例Bean的生命周期: (容器启动)构造器------>初始化方法---->容器关闭(销毁方法)
- 多例Bean的生命周期: 获取Bean(构造器---->初始化方法)---->容器关闭不会调用Bean的销毁方法
- 后置处理器----在Bean初始化前面调用该方法
- 单例:(容器启动)构造器--->后置处理器before---->初始化方法---->后置处理器的after方法----》容器关闭(销毁方法)
- 无论bean是否有初始化方法,后置处理器都会默认其有,还会继续工作
- Bean(对象)实例化的三种方式
- 工厂静态方法实例化Bean(对象)---返回的是Bean对象,而不是工厂对象
- 工厂的实例方法
- FactoryBean: 是Spring规定的一个接口,只要是这个接口的实现类,Spring都默认是一个工厂,并且无论指定工厂中创建的对象是单例还是多例,都是在获取的时候才会创建对象,IOC容器启动的时候不会创建实例
- 依赖注入方式
- set方式
- 通过xml配置方式完成对属性的赋值,基本都使用set方式,切莫忘记在对应的类中给属性添加set方法
- set简便注入方式---P命名空间注入
- 有参构造注入时,不会再调用Bean对象的无参构造,直接走对应的有参构造
- 调用有参构造器创建对象时,如果写了name属性,那么可以编制有参构造的参数顺序来为属性赋值,但是被赋值的参数个数,必须与对象中存在的有参构造的参数个数匹配,例如:我在类中只写了一个有两个参数的有参构造,那么在调用有参构造赋值的时候,必须给指定的两个参数赋值
- 如果在调用有参构造创建对象的时候省略了name属性,那么必须严格按照构造器的参数的顺序挨个赋值
- 调用有参构造创建对象的时候,可以通过index指定参数的索引,从0开始,并且如果出现了有参构造重载,还可以利用type指定参数的类型
- Bean的依赖注入的数据类型
- 普通数据类型的注入----通过类的set方法
- 集合的注入
- 引入其他配置文件,分模块开发----import标签
- Spring重点配置
- ApplicationContext的继承体系
- ApplicationContext的实现类
- getBean()两个重载方法的使用
- 两种方法的区别
- Spring配置数据源---连接池
- 数据库连接池作为单实例是最好的,一个项目就一个连接池,连接池里面管理很多连接,连接是直接从连接池拿,因此可以让Spring帮我们创建连接池对象
- 数据源(连接池)的作用
- 数据源的开发步骤
- 数据源的手动创建
- 抽取jdbc.properties文件
- Spring配置数据源
- 按照类型获取组件,可以获取到这个类型下所有实现的子类---DataSource类是所有数据连接池对象的基类
- Spring加载properties配置文件----将配置文件相关内容放到容器中
- 加载外部配置文件的固定写法classpath: 表示引用类路径下的一个资源
- ${}动态取出配置文件中某个key对应的值,但是注意username是Spring的key的一个关键字,为了防止配置文件中的key和spring自己的关键字冲突,起名的时候一般会加上一个防止冲突的前缀
- Spring注解开发
- Spring原始注解
- 注解的组件扫描
- 代码演示
- 在进行组件扫描的时候,还是需要引入context命名空间,配置好基础包后,Spring会扫描基础包及其子包
- 注解后面不标注ID,那么默认ID是类名的首字母小写
- 组件的作用域默认是单例的,可以通过scope注解进行修改
- xml方式注入和注解方式注入的一些区别
- @Autowired和 @Qualifier的原理
- @Autowired标注的自动装配的属性默认是一定装配上的,如果不能装配上,会报错
- @Autowired(required=false):如果无法装配上,赋值为Null
- @Autowired自动注入只针对引用类型生效,普通属性赋值使用value注解
- @Autowired可以标注在方法上,@Qualifier可以标注在方法的形参上,装配原理和上面一致
- @Autowired标注的方法,会在单例Bean创建的时候自动调用,即容器创建的时候调用
- @Autowired注解详细使用规则参加下面两篇文章
- @Autowired当标注的属性是接口时,其实注入的是这个接口的实现类,具体细节参考上面这篇文章链接
- resource注解
- @Resource和@Autowired的区别
- 通过注解对属性完成注入---value注解
- @Value注解常用方式---配合配置文件
- 注意: xml中需要引入相关配置文件以及组件扫描
- @Value注解常用方式---配合配置文件
- scope注解----设置对象的作用范围
- postConstruct和predestory注解
- Spring新注解
- 核心配置类
- 数据源配置类
- 测试类
- Spring集成Junit
- 原始Junit测试Spring的问题
- 解决思路
- Spring集成Junit的步骤
- spring单元测试原理(ContextConfiguration和runwith注解)
- 代码实现
- 泛型依赖注入原理图
- IOC部分总结
- Spring集成web环境
- 导入servlet和jsp的坐标
- 监听器的妙用---加载配置文件
- 针对创建app对象时,xml配置文件路径写死的优化
- 通过监听器的全局参数来进行优化
- 针对在获取上下文对象时,属性名写死的优化
- 针对创建app对象时,xml配置文件路径写死的优化
- Spring提供获取应用上下文的工具---上面是铺垫
- 我们需要做的事情
- AOP
- 什么是AOP
- AOP底层实现
- AOP动态代理技术
- JDK动态代理实例演示
- cglib动态代理实例演示
- AOP相关概念
- 连接点可以理解为可以被增强的方法
- 切入点(切点): 对哪些连接点进行了配置,完成了对方法的增强,可以理解为被增强了的方法
- advice: 简单理解为对要增强的方法中的增强逻辑的封装,封装为一个对象,这个对象就是增强对象,增强对象的方法就是增强逻辑
- 切面: 目标方法加逻辑增强
- 织入: 切点和增强结合的过程
- AOP开发明确事项
- 知识要点
- 基于XML的AOP开发
- 速入门的步骤
- 1.导入aspectj的坐标
- 2.目标接口和目标类
- 接口不用加载到容器中,即使加载到了容器中,也不会创建对象,相当于告诉了Spring容器,ioc容器中可能有这种类型的组件
- 3.切面类
- 4.目标类和切面类交给Spring容器去创建
- 5.在app.xml中配置织入关系---引入aop命名空间
- 6.测试代码
- 如果目标类有实现接口,那么AOP底层原理即使jdk动态代理,容器中保存的组件是其代理对象,而非本类类型,因此再使用getBean获取实例的时候,如果通过字节码文件类型获取,参数应该填入接口类型,返回值也用接口类型来接
- 如果目标类没有实现接口,那么AOP底层实现原理就是cglib,我们可以用目标类去接,相当于多态形式
- 切点表达式的写法
- 切点表达式支持的通配符
- 通知的类型
- 通知的配置语法
- before,after,aroud演示
- after-throwing
- after---最终增强,不管是否抛出异常,都会执行
- 切点表达式的抽取
- xml配置中也可以在指定前置...方法的时候,通过设置returning和throwing参数,来获取相应的返回值和异常信息
- 抽取后得到的pointcut标签,如果写在了当前的切面类里面,那么也只能够在当前切面类里面引用,如果想扩大作用域范围,可以将标签写在config表签下面,相当于一个全局变量
- 可以给aspect标签里面加上order属性,来指定切面的执行顺序
- 知识要点
- 速入门的步骤
- 基于注解的AOP开发
- 快速入门的步骤
- 别忘记开启基于注解的aop功能--->aop自动代理
- 注解通知类型
- 通知方法的执行顺序
- 在通知方法运行时,拿到目标方法的详细信息----JoinPoint
- throwing和returning来指定哪个参数用来接收异常和返回值
- spring通知方法的参数列表一定不能乱写
- 上面returning和throwing用来接收异常和返回值信息的指定参数的数据类型,最好往大了写,不然可能无法接收到数据
- 切点表达式的抽取----随便声明一个返回值为void的空方法
- 可以使用环绕通知完成四合一功能: 前置,返回,异常,后置
- 如果在使用环绕通知时,代码里面对异常进行了抓取,为了能让外界知道这个异常,这个异常一定要抛出去
- 环绕通知优先于普通通知执行
- 如果想要通过通知来实现动态代理的功能,那么就可以选择环绕通知,利用里面的ProceedingJoinPoint 类型的对象,完成对方法执行的干预
- 多切面运行顺序
- 环绕只影响当前切面,他的优先执行顺序只体现在它所在的当前切面,与其他切面无关
- AOP的使用场景
- SpringJDBCTemplte
- JDBCTemplte开发步骤
- 1.导入spring-jdbc和spring-tx以及其他的一些坐标
- 创建数据源,设置数据源,执行操作
- 通过Spring让我们产生模板对象
- 抽取数据库连接池配置时填入的参数,放到properties配置文件中
- 在Spring容器中引入pro配置文件,然后修改刚才传入的参数---配置数据库的模板
- JDBCTemplte开发步骤
Spring体系结构
Spring程序开发步骤
1.创建一个maven项目,在pom.xml中导入spring的坐标
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SpringDemo2</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.15.RELEASE</version>
</dependency>
</dependencies>
</project>
2.创建UserDao接口和UserDao接口的实现类UserDaoImpl 3.创建spring的xml配置文件,然后在其中给实现类标注id
代码语言:javascript复制 <bean id="userDao" class="com.impl.UserDaoImpl"></bean>
- 创建一个测试Demo,查看是否能够获取指定实现类对象,并调用其方法
public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.say();
}
}
Spring配置文件
Bean标签的基本配置
Bean标签的范围配置
默认是singleton
默认情况下演示:
代码语言:javascript复制 <bean id="userDao" class="com.impl.UserDaoImpl"></bean>
代码语言:javascript复制 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
UserDao userDao1=(UserDao)app.getBean("userDao");
System.out.println(userDao);
System.out.println(userDao1);
默认单例模式
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
意思是加载配置文件,创建Spring容器
内部Bean----匿名,外部无法访问,无别名
1.在或内部通过定义的, 2.该bean不管是否指定id或者name,该bean都有一个唯一的匿名标识符,且不能被指定别名 3.该bean队其他外部的bean不可见。
util名称空间—创建集合的id,方便引用
代码语言:javascript复制Util名称空间创建的集合有id,可以给其他bean引用,在使用前需要引入名称空间: xmlns:util=“http://www.springframework.org/schema/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-4.0.xsd">
<!-- 相当于new LinkedHashMap() -->
<util:map id="myMap">
<entry key="key01" value="value01"></entry>
</util:map>
<bean id="person" class="com.atguigu.bean.Person">
<!-- 引用myMap ->
<property name="map" ref="myMap"></property>
</bean>
</beans>
级联属性----选择属性的属性
通过属性(a)选择属性(a)的属性(b)
代码语言:javascript复制 <bean id="car01" class="com.atguigu.bean.Car">
<property name="carName" value="宝马"></property>
</bean>
<!-- 级联属性:属性的属性 -->
<bean id="person01" class="com.atguigu.bean.Person">
<!-- 为car赋值的时候改变car的属性 -->
<property name="car" ref="car01"></property>
<!-- 给car属性的carName属性赋值 -->
<property name="car.carName" value="奔驰"></property>
</bean>
ref引用是地址引用,可以理解为c 里面的地址传递
继承配置信息
在bean定义中含了大量的配置信息,其中包括容器相关的信息(比如初始化方法、静态工厂方法名等等)以及构造器参数和属性值。子bean定义就是从父bean定义继承配置数据的bean定义。它可以覆盖父bean的一些值,或者添加一些它需要的值。使用父/子bean定义的形式可以节省很多的输入工作。实际上,这就是一种模板形式。
class全类名也会继承,但是只是继承配置信息,而不是父子关系
全类名省略不写的前提时,当前bean对象的类型与继承的bean类型一致
如果需要对继承的数据进行修改,就自行对相关属性再赋值,完成值的覆盖
代码语言:javascript复制 <bean id="parent" class="com.timo.domain.Parent">
<property name="name" value="ouyangfeng"/>
</bean>
<!--下面的parent表示这个child的bean的父亲是id=parent的这个类-->
<bean id="child" class="com.timo.domain.Child" parent="parent">
<property name="age" value="18"/>
</bean>
abstract—当前bean只能被其他bean继承相关配置数据,而无法创建实例化的bean对象,不用写全类名
下面说的就是: 抽象bean不必映射到任何类,即不用写全类名
Spring抽象bean abstract=true声明
IOC容器创建时候,容器中的所有Bean对象也会随之创建,并且Bean标签就等同于new 一个Bean对象,会在创建的Bean对象的时候,调用其无参构造。
Bean的创建顺序是按照xml中配置顺序创建的
Spring 给Bean属性注入null值
在容器中注册一个组件(Bean标签就是组件的注册,等同于new一个Bean对象)时,同一个组件(对象)默认是单例的,容器启动完成,容器中所有组件创建完毕
IOC容器在创建组件对象的时候,如果使用Property标签对属性进行赋值,那么默认利用对象的setter属性进行赋值
javaBean对象的属性名由对象的setter方法,去掉set后面,后面的那一串首字母小写就是属性名
getBean方法的三个重置版本
代码语言:javascript复制getBean(people.class);//类型查找
getBean("people01");//ID查找
getBean("people01",people.class);//ID加类型查找
单例情况下演示:
代码语言:javascript复制 <bean id="userDao" class="com.impl.UserDaoImpl" scope="singleton"></bean>
多例演示:
代码语言:javascript复制 <bean id="userDao" class="com.impl.UserDaoImpl" scope="prototype"></bean>
多例模式每一次获取的对象都不相同
注意
Bean(对象)的生命周期配置
UserDaoImpl类:
代码语言:javascript复制package com.impl;
import com.UserDao;
public class UserDaoImpl implements UserDao {
@Override
public void say() {
System.out.println("用户登录");
}
void init()
{
System.out.println("初始化中...");
}
void destory()
{
System.out.println("被销毁中....");
}
}
配置文件:
代码语言:javascript复制 <bean id="userDao" class="com.impl.UserDaoImpl" init-method="init" destroy-method="destory" ></bean>
测试类:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
System.out.println(userDao);
//需要强转成其子类,才能调用close方法,来关闭容器
((ClassPathXmlApplicationContext)app).close();
}
}
容器没有关闭,所以对象没有被释放,也就不会去调用销毁方法
单例Bean的生命周期: (容器启动)构造器------>初始化方法---->容器关闭(销毁方法)
多例Bean的生命周期: 获取Bean(构造器---->初始化方法)---->容器关闭不会调用Bean的销毁方法
后置处理器----在Bean初始化前面调用该方法
单例:(容器启动)构造器—>后置处理器before---->初始化方法---->后置处理器的after方法----》容器关闭(销毁方法)
无论bean是否有初始化方法,后置处理器都会默认其有,还会继续工作
代码语言:javascript复制//1.编写后置处理器的实现类
//2.将后置处理器注册在配置文件中
public class MyBeansProcess implements BeanPostProcessor {
//初始化之前调用
//Object bean: 将要初始化的bean
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName "Bean将要调用初始化方法" " " bean);
return bean;//返回传入的bean
}
//String beanName: bean在xml中配置的id
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName "Bean初始化方法调用完毕" " " bean);
//初始化之后返回的是什么,容器中保存的就是什么
return bean;
}
}
配置文件:
代码语言:javascript复制<bean id="book" class="com.dhy.Factory.book"/>
<bean id="beanPostProcess" class="com.dhy.Factory.MyBeansProcess"/>
book类;
代码语言:javascript复制public class book {
book()
{
System.out.println("book的初始化方法");
}
@Override
public String toString() {
return "图书";
}
}
测试类:
代码语言:javascript复制public class main {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("factory.xml");
System.out.println(app.getBean("book"));
if(app.getBean("book")!=null)
System.out.println("NO");
else
System.out.println("YES");
}
}
Bean(对象)实例化的三种方式
工厂静态方法实例化Bean(对象)—返回的是Bean对象,而不是工厂对象
工厂类:
代码语言:javascript复制public class sf {
public static UserDao getUserDao()
{
return new UserDaoImpl();
}
}
配置文件:
当添加了factory-method属性后,就会去找当前全类名对应下面的getUserDao方法,返回对应的对象
代码语言:javascript复制 <bean id="userDao" class="com.fac.staticFac.sf" factory-method="getUserDao"></bean>
测试类:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
System.out.println(userDao);
//需要强转成其子类,才能调用close方法,来关闭容器
((ClassPathXmlApplicationContext)app).close();
}
}
工厂的实例方法
工厂类:
代码语言:javascript复制public class sf {
public UserDao getUserDao()
{
return new UserDaoImpl();
}
}
配置文件:
在查找到userDao的ID时,会去查查找ID为f的bean标签,然后创建工厂对象,然后调用其方法,返回一个userDao对象
代码语言:javascript复制 <bean id="f" class="com.fac.staticFac.sf" ></bean>
<bean id="userDao" factory-bean="f" factory-method="getUserDao"></bean>
测试类代码不变,:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
System.out.println(userDao);
//需要强转成其子类,才能调用close方法,来关闭容器
((ClassPathXmlApplicationContext)app).close();
}
}
实现了接口的工厂类:
代码语言:javascript复制/*
* 1.编写一个FactoryBean的实现类
* 2.在Spring配置文件中进行注册
* */
public class factory implements FactoryBean {
/*工厂方法,返回创建的对象
* */
public Object getObject() throws Exception {
Book b=new Book();
return b;
}
/*返回创建的对象的类型,Spring会自动调用这个方法来确认创建的对象是什么类型
* */
public Class<?> getObjectType() {
return Book.class;
}
//是单例吗 true:是单例 false:不是单例
public boolean isSingleton() {
return false;
}
}
配置文件:
代码语言:javascript复制<bean id="myFacBeanImple" class="com.dhy.Factory.factory"/>
测试类:
代码语言:javascript复制public class main {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("factory.xml");
//返回的对象是工厂方法里面返回的对象
System.out.println(app.getBean("myFacBeanImple"));
}
}
依赖注入方式
UserService类中需要用到User类对象,因此需要将User类注入到UserService类中,下面演示:
set方式
通过xml配置方式完成对属性的赋值,基本都使用set方式,切莫忘记在对应的类中给属性添加set方法
配置文件:
代码语言:javascript复制 <bean id="userDao" class="com.User.User"></bean>
<!--property标签写在被注入的类标签内部,即要设置谁的属性,就写在谁的bean标签内部-->
<bean id="userService" class="com.User.UserService">
<!--通过属性注入,这里name填入的是截取set方法后set部分后,剩余部分变小写的值,即属性的名字-->
<!--ref是要注入哪个类,这里注入的是userDao的ID表示的User类-->
<property name="user" ref="userDao"></property>
</bean>
userService类:
代码语言:javascript复制public class UserService {
private User user;
public void setUser(User user) {
this.user = user;
}
public void show()
{
//在容器内部,将user对象注入UserService,即通过UserService的set方法,给US的成员变量user赋值
user.show();
}
}
user类:
代码语言:javascript复制public class User {
public void show()
{
System.out.println("大忽悠来了");
}
}
测试类:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService us=(UserService)app.getBean("userService");
us.show();
}
}
注意:只有从容器内部拿出来的UserService才通过US类的setUser方法,完成了对user对象的赋值,即注入,直接new一个,没有用,因此其没有被注入user对象,会报空指针异常
set简便注入方式—P命名空间注入
第一步: 引入p命名空间
代码语言:javascript复制xmlns:p="http://www.springframework.org/schema/p"
第二步: 修改注入方式
注意: userDao-ref是用来注入对象的,而userDao是用来注入普通属性的
代码语言:javascript复制 <bean id="userDao" class="com.User.User"></bean>
<bean id="userService" class="com.User.UserService"
p:user-ref="userDao">
</bean>
有参构造注入时,不会再调用Bean对象的无参构造,直接走对应的有参构造
调用有参构造器创建对象时,如果写了name属性,那么可以编制有参构造的参数顺序来为属性赋值,但是被赋值的参数个数,必须与对象中存在的有参构造的参数个数匹配,例如:我在类中只写了一个有两个参数的有参构造,那么在调用有参构造赋值的时候,必须给指定的两个参数赋值
如果在调用有参构造创建对象的时候省略了name属性,那么必须严格按照构造器的参数的顺序挨个赋值
调用有参构造创建对象的时候,可以通过index指定参数的索引,从0开始,并且如果出现了有参构造重载,还可以利用type指定参数的类型
userService类:
代码语言:javascript复制public class UserService {
private User user;
public UserService(User user) {
this.user = user;
}
public UserService() {
}
public void show()
{
//在容器内部,将user对象注入UserService,即通过UserService的set方法,给US的成员变量user赋值
user.show();
}
}
配置文件:
代码语言:javascript复制 <bean id="userDao" class="com.User.User"></bean>
<bean id="userService" class="com.User.UserService">
<constructor-arg name="user" ref="userDao"></constructor-arg>
</bean>
测试类:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService us=(UserService)app.getBean("userService");
us.show();
}
}
Bean的依赖注入的数据类型
普通数据类型的注入----通过类的set方法
代码语言:javascript复制给User的name和age属性注入两个值
package com.User;
public class User
{
private String name;
private int age;
public void show()
{
System.out.println("姓名: " name " 年龄: " age);
}
public void setName(String name) {
this.name=name;
}
public void setAge(int age) {
this.age=age;
}
}
配置文件:
代码语言:javascript复制//这里是通过setName<---Name<---name来找到对应的set方法,进行属性的设置
<bean id="userDao" class="com.User.User">
<property name="name" value="大忽悠"/>
<property name="age" value="18" />
</bean>
测试:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
User us=(User)app.getBean("user");
us.show();
}
}
集合的注入
ref是引用,被引用的内容,必须已经存在于Spring容器中
被注入的类:
代码语言:javascript复制public class UserService {
private List<String> list;
private Map<String,User> map;
private Properties properties;
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, User> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
void show()
{
System.out.println(list);
System.out.println(map);
System.out.println(properties);
}
}
map中的user对象
代码语言:javascript复制public class User
{
private String name;
private int age;
public void setName(String name) {
this.name=name;
}
public void setAge(int age) {
this.age=age;
}
}
配置文件:
代码语言:javascript复制 <bean id="user" class="com.User.UserService">
<property name="list" >
<list>
<!--普通类型,不是引用类型,用value-->
<value>大忽悠</value>
<value>和</value>
<value>小朋友</value>
</list>
</property>
<property name="map">
<map>
<entry key="用户:" value-ref="user1" ></entry>
<entry key="用户:" value-ref="user2" ></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="p1">ppp1</prop>
<prop key="p3">ppp2</prop>
<prop key="p2">ppp3</prop>
</props>
</property>
</bean>
<bean id="user1" class="com.User.User">
<!-- name-Name-setName-->
<property name="name" value="Tom"></property>
<property name="age" value="18"></property>
</bean>
<bean id="user2" class="com.User.User">
<!-- name-Name-setName-->
<property name="name" value="Booby"></property>
<property name="age" value="19"></property>
</bean>
测试类:
代码语言:javascript复制public class testDemo
{
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService us=(UserService) app.getBean("user");
us.show();
}
}
引入其他配置文件,分模块开发----import标签
Spring重点配置
ApplicationContext的继承体系
ApplicationContext的实现类
getBean()两个重载方法的使用
第二种传入一个字节码对象类型演示:
代码语言:javascript复制 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = app.getBean(UserService.class);
bean.show();
两种方法的区别
1.通过获取字节码文件对象,获取的返回值,不用进行强制类型转换
2.当出现多个类型相同的bean(对象)时,只能使用第一种方式
Spring配置数据源—连接池
数据库连接池作为单实是最好的,一个项目就一个连接池,连接池里面管理很多连接,连接是直接从连接池拿,因此可以让Spring帮我们创建连接池对象
数据源(连接池)的作用
数据源的开发步骤
数据源的手动创建
手动创建c3p0数据源
代码语言:javascript复制 /*手动创建c3p0数据源*/
@Test
public void test1() throws PropertyVetoException, SQLException {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("126433");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
手动创建druid数据源
代码语言:javascript复制 /*手动创建druid数据源*/
@Test
public void test2() throws PropertyVetoException, SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/test");
druidDataSource.setUsername("root");
druidDataSource.setPassword("126433");
DruidPooledConnection connection = druidDataSource.getConnection();
System.out.println(connection);
connection.close();
}
抽取jdbc.properties文件
jdbc.properties配置文件:
代码语言:javascript复制jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test1
jdbc.username=root
jdbc.password=126433
演示:
代码语言:javascript复制 /*手动创建c3p0数据源(加载配置文件形式)*/
@Test
public void test1() throws PropertyVetoException, SQLException {
//读取配置文件
//参数: 基名,相对于类加载路径地址---resource文件下面的地址
//不需要扩展名
ResourceBundle rb=ResourceBundle.getBundle("jdbc");
String driver=rb.getString("jdbc.driver");
String url=rb.getString("jdbc.url");
String username=rb.getString("jdbc.username");
String password=rb.getString("jdbc.password");
//创建数据源对象,设置连接参数
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
Connection connection=dataSource.getConnection();
System.out.println(connection);
connection.close();
}
Spring配置数据源
可以将DataSource的创建权交给Spring容器去完成
第一步: 导入坐标
代码语言:javascript复制 <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.15.RELEASE</version>
</dependency>
第二步: 配置文件中完成注入
代码语言:javascript复制<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test1"></property>
<property name="user" value="root"></property>
<property name="password" value="126433"></property>
</bean>
第三步: 测试
按照类型获取组件,可以获取到这个类型下所有实现的子类—DataSource类是所有数据连接池对象的基类
代码语言:javascript复制 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource bean = app.getBean(DataSource.class);
Connection connection=bean.getConnection();
System.out.println(connection);
connection.close();
Spring加载properties配置文件----将配置文件相关内容放到容器中
第一步: 引入命名空间和约束路径
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<!--上面一行copy一份,前面加个context前缀,把后面的beans改成context-->
xmlns:context="http://www.springframework.org/schema/context"
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 https://www.springframework.org/schema/util/spring-util.xsd
<!--上面一行copy一份,把后面的beans都改成context-->
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
第二步: 加载配置文件
加载外部配置文件的固定写法classpath: 表示引用类路径下的一个资源
代码语言:javascript复制 <!--加载外部的properties文件-->
<!--当前要加载的properties文件在资源文件下,前面需要加上classpath:-->
<context:property-placeholder location="classpath:jdbc.properties"/>
第三步: 完成对数据库连接池对象的相关信息注入
${}动态取出配置文件中某个key对应的值,但是注意username是Spring的key的一个关键字,为了防止配置文件中的key和spring自己的关键字冲突,起名的时候一般会加上一个防止冲突的前缀
代码语言:javascript复制 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--通过键值的方式引入值-->
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
jdbc.properties配置文件:
代码语言:javascript复制jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test1
jdbc.username=root
jdbc.password=126433
第四步:测试
代码语言:javascript复制 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource bean = app.getBean(DataSource.class);
Connection connection=bean.getConnection();
System.out.println(connection);
connection.close();
Spring注解开发
Spring原始注解
注解的组件扫描
代码演示
环境准备:
需要注入的类:
代码语言:javascript复制//<bean id="userDao" class="com.User.User"></bean>
@Component("userDao")
public class User
{
public void show()
{
System.out.println("User注入完成");
}
}
被注入的类:
代码语言:javascript复制//<bean id="user" class="com.User.UserService">
@Component("user")
public class UserService {
// <property name="user" ref="userDao"></property>
@Autowired
@Qualifier("userDao")
private User user;
public void setUser(User user) {
this.user = user;
}
public void show()
{
user.show();
}
}
在进行组件扫描的时候,还是需要引入context命名空间,配置好基础包后,Spring会扫描基础包及其子包
注解后面不标注ID,那么默认ID是类名的首字母小写
组件的作用域默认是单例的,可以通过scope注解进行修改
组件扫描配置:
代码语言:javascript复制<!--配置组件扫描-->
<context:component-scan base-package="com"/>
测试:
代码语言:javascript复制 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = app.getBean(UserService.class);
bean.show();
xml方式注入和注解方式注入的一些区别
如果是xml方式配置,那么在进行对象注入的时候,被注入的类,需要提供相关成员变量的set方法
如果是注解配置,我们把注解放到属性,即成员变量上面后,注解会直接通过反射给成员变量赋值,这样就不需要set方法了
如果按照类型注入,只需要写Autowired,前提是对应的数据类型在容器中的对象只有一个 如果按照id注入,那么还需要在上面加上Autowired注解
代码语言:javascript复制@Component("user")
public class UserService {
@Autowired//按照数据类型从Spring容器中进行匹配,如果数据类型的对象只有一个
//下面的qualifier可以省略不写,如果存在多个相同数据类型的对象,则会产生二义性
@Qualifier("userDao")//按照id的名称,从容器中进行匹配的
private User user;
public void show()
{
user.show();
}
}
@Autowired和 @Qualifier的原理
如果资源类型的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean,进行装配
@Qualifier:指定一个名作为id,让spring别使用变量名作为id
@Autowired标注的自动装配的属性默认是一定装配上的,如果不能装配上,会报错
@Autowired(required=false):如果无法装配上,赋值为Null
@Autowired自动注入只针对引用类型生效,普通属性赋值使用value注解
@Autowired可以标注在方法上,@Qualifier可以标注在方法的形参上,装配原理和上面一致
@Autowired标注的方法,会在单例Bean创建的时候自动调用,即容器创建的时候调用
@Autowired注解详细使用规则参加下面两篇文章
使用@Autowired注解完成属性依赖注入时,写在属性上与写在set方法上的区别 @Autowired注解详解——超详细易懂
@Autowired当标注的属性是接口时,其实注入的是这个接口的实现类,具体细节参考上面这篇文章链接
resource注解
resource相当于Autowired加上Qualifier
代码语言:javascript复制@Component("user")
public class UserService {
@Resource(name="userDao")
private User user;
public void show()
{
user.show();
}
}
@Resource和@Autowired的区别
简单使用演示:
代码语言:javascript复制@Component("user")
public class UserService {
@Value("18")
private int age;
@Resource(name="userDao")
private User user;
public void show()
{
System.out.println("年龄为:" age);
}
}
@Value注解常用方式—配合配置文件
代码语言:javascript复制@Component("user")
public class UserService {
@Value("${jdbc.driver}")
private String driver;
@Resource(name="userDao")
private User user;
public void show()
{
System.out.println(driver);
}
}
注意: xml中需要引入相关配置文件以及组件扫描
代码语言:javascript复制<!--配置组件扫描-->
<context:component-scan base-package="com"/>
<!--加载外部的properties文件-->
<!--当前要加载的properties文件在资源文件下,前面需要加上classpath:-->
<context:property-placeholder location="classpath:jdbc.properties"/>
scope注解----设置对象的作用范围
一个类上面只能添加一个scope注解
代码语言:javascript复制//一个类上面只能添加一个scope注解
//@Scope("prototype")
@Scope("singleton")
public class testDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = app.getBean(UserService.class);
bean.show();
}
}
postConstruct和predestory注解
代码语言:javascript复制@Component("user")
public class UserService
{
public UserService() {
System.out.println("构造方法执行中...");
}
@PostConstruct
void init()
{
System.out.println("初始化中...");
}
@PreDestroy
void destory()
{
System.out.println("销毁中....");
}
public void show()
{
System.out.println("show方法调用");
}
}
容器没有关闭,所以对象没有被释放,也就不会去调用销毁方法,因此只有在调用了close方法,容器被关闭后,对象才会被释放,才会去调用销毁方法
测试类:
代码语言:javascript复制public class testDemo {
public static void main(String[] args) {
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = app.getBean(UserService.class);
bean.show();
app.close();
}
}
Spring新注解
核心配置类
代码语言:javascript复制//标志该类是Spring的核心配置类
@Configuration
//<context:component-scan base-package="com"/>
//相当于配置组件扫描
@ComponentScan("com")
//<import resource=""/> 通过import将数据域相关的配置加载到核心(总)配置中
@Import(DataSourceCofiguration.class)
//如果要加载多个配置
//@Import({DataSourceCofiguration.class,...})
public class SpringCofiguration {}
数据源配置类
代码语言:javascript复制//<context:property-placeholder location="classpath:jdbc.properties"/>
//加载配置文件进Spring容器
@PropertySource("classpath:jdbc.properties")
public class DataSourceCofiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String pasword;
@Bean("dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws PropertyVetoException, SQLException {
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(pasword);
Connection connection = dataSource.getConnection();
System.out.println("执行getDataSource方法,返回connection:" connection);
connection.close();
return dataSource;
}
}
测试类
代码语言:javascript复制public class test {
public static void main(String[] args) throws SQLException {
ApplicationContext app= new AnnotationConfigApplicationContext(SpringCofiguration.class);
app.getBean(UserService.class).show();
}
}
Spring集成Junit
原始Junit测试Spring的问题
解决思路
Spring集成Junit的步骤
spring单元测试原理(ContextConfiguration和runwith注解)
代码实现
第一步: 在pom.xml中导入Spring-test的坐标
代码语言:javascript复制 <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
第二步: 创建测试类
代码语言:javascript复制@RunWith(SpringJUnit4ClassRunner.class)//让测试运行于Spring测试环境
//通过读取配置文件,在测试类中完成对Bean的注入
//@ContextConfiguration("classpath:applicationContext.xml")
//通过加载核心配置类,完成注入操作
@ContextConfiguration(classes = SpringCofiguration.class)//如果要加载多个,使用数组形式
public class JunitTest
{
@Autowired
private UserService us;
@Autowired
private DataSource ds;
@Test
public void test() throws SQLException {
us.show();
System.out.println(ds.getConnection());
}
}
泛型依赖注入原理图
IOC部分总结
Spring集成web环境
导入servlet和jsp的坐标
代码语言:javascript复制 <dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
第二步: 完善web目录结构
第三步: xml文件配置(也可以使用注解配置)
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>Web.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>
</web-app>
第四步: 通过xml配置,在容器中创建单例的userservice对象
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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 https://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="userService" class="com.User.UserService"></bean>
</beans>
userServlet类进行测试:
代码语言:javascript复制public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = app.getBean(UserService.class);
bean.show();
}
}
tomcat服务器别忘记部署
监听器的妙用—加载配置文件
要解决的问题:
解决方法:
代码:
监听器类:
代码语言:javascript复制public class LS implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
//将Spring的应用上下文对象存储到ServletContext域中
ServletContext servletContext = servletContextEvent.getServletContext();
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建完毕");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
web.xml中配置监听器
代码语言:javascript复制 <!--配置监听器-->
<listener>
<listener-class>Listener.LS</listener-class>
</listener>
userServlet类中通过从ServletContext域中拿到存放数据,来取出上下文对象,从而从Spring容器中拿出单例对象US
代码语言:javascript复制public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
ApplicationContext app = (ApplicationContext)servletContext.getAttribute("app");
UserService bean = app.getBean(UserService.class);
bean.show();
}
}
针对创建app对象时,xml配置文件路径写死的优化
代码语言:javascript复制 ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
通过监听器的全局参数来进行优化
web.xml配置文件:
代码语言:javascript复制 <!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
监听器类:
代码语言:javascript复制 @Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
//读取web.xml中的全局参数
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
ApplicationContext app=new ClassPathXmlApplicationContext(contextConfigLocation);
//将Spring的应用上下文对象存储到ServletContext域中
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建完毕");
}
针对在获取上下文对象时,属性名写死的优化
WebAppUtils工具类:
代码语言:javascript复制public class WebAppUtils {
static public ApplicationContext getApp(ServletContext sc)
{
return (ApplicationContext)sc.getAttribute("app");
}
}
UserServlet类:
代码语言:javascript复制public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
ApplicationContext app = WebAppUtils.getApp(servletContext);
UserService bean = app.getBean(UserService.class);
bean.show();
}
}
Spring提供获取应用上下文的工具—上面是铺垫
我们需要做的事情
Spring-web坐标:
代码语言:javascript复制 <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
web.xml配置:
代码语言:javascript复制 <!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
UserServlet类:
代码语言:javascript复制public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService bean = app.getBean(UserService.class);
bean.show();
}
}
AOP
什么是AOP
AOP底层实现
AOP动态代理技术
JDK动态代理实例演示
TargetInterface接口:
代码语言:javascript复制public interface TargetInterface {
void show();
}
target类:
代码语言:javascript复制public class Target implements TargetInterface {
@Override
public void show() {
System.out.println("show方法调用");
}
}
advice增强类:
代码语言:javascript复制public class Advice {
void berfore()
{
System.out.println("前置增强");
}
void after()
{
System.out.println("后置增强");
}
}
main测试类:
代码语言:javascript复制public class main {
public static void main(String[] args) {
//目标对象
Target target=new Target();
//增强对象
Advice advice=new Advice();
Object p = Proxy.newProxyInstance(
//目标对象类加载器
target.getClass().getClassLoader(),
//目标对象相同的接口字节码对象数组
target.getClass().getInterfaces(),
//目标方法执行器,帮我们目标对象执行目标方法
new InvocationHandler() {
//调用代理对象的任何方法,实际都是执行invoke方法
@Override
//proxy: 给jdk使用的,任何时候都不要动这个对象
//method:当前要执行的目标对象的方法
//args:这个方法调用时外界传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.berfore();//前置增强
//利用反射执行目标方法
//目标方法执行后的返回值
Object invoke = method.invoke(target, args);
advice.after();//后置增强
//返回值必须真正返回出去,外界才能拿到执行后的返回值
return invoke;
}
}
);
//调用代理对象的方法
((TargetInterface)p).show();
}
}
cglib动态代理实例演示
Spring五版本,已经将cglib相关jar包放入core的核心包里面了
target类:
代码语言:javascript复制public class Target {
public void show() {
System.out.println("show方法调用");
}
}
advice增强类:
代码语言:javascript复制public class Advice {
void berfore()
{
System.out.println("前置增强");
}
void after()
{
System.out.println("后置增强");
}
}
main测试主类:
代码语言:javascript复制public class main {
public static void main(String[] args) {
//目标对象
Target target=new Target();
//增强对象
Advice advice=new Advice();
//返回值就是动态代理生成的对象,基于cglib
//1,创建增强器
Enhancer enhancer=new Enhancer();
//2.设置父类(目标对象)
enhancer.setSuperclass(Target.class);
//3.设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//前置增强
advice.berfore();
//执行目标
Object invoke = method.invoke(target, args);
//后置增强
advice.after();
return invoke;
}
});
//4.创建代理对象
Target proxy = (Target) enhancer.create();
proxy.show();
}
}
AOP相关概念
连接点可以理解为可以被增强的方法
切入点(切点): 对哪些连接点进行了配置,完成了对方法的增强,可以理解为被增强了的方法
advice: 简单理解为对要增强的方法中的增强逻辑的封装,封装为一个对象,这个对象就是增强对象,增强对象的方法就是增强逻辑
切面: 目标方法加逻辑增强
织入: 切点和增强结合的过程
AOP开发明确事项
知识要点
基于XML的AOP开发
快速入门的步骤
1.导入aspectj的坐标
代码语言:javascript复制 <!--导入aspectj的坐标-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.4</version>
</dependency>
2.目标接口和目标类
接口不用加载到容器中,即使加载到了容器中,也不会创建对象,相当于告诉了Spring容器,ioc容器中可能有这种类型的组件
代码语言:javascript复制public interface TargetInterface {
void show();
}
public class Target implements TargetInterface{
public void show() {
System.out.println("Target's show");
}
}
3.切面类
代码语言:javascript复制public class MyAspect {
public void before()
{
System.out.println("前置增强");
}
}
4.目标类和切面类交给Spring容器去创建
代码语言:javascript复制 <!--配置目标对象-->
<bean id="target" class="com.dhy.aop.Target"></bean>
<!--配置切面-->
<bean id="myAspect" class="com.dhy.aop.MyAspect"></bean>
5.在app.xml中配置织入关系—引入aop命名空间
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置目标对象-->
<bean id="target" class="com.dhy.aop.Target"></bean>
<!--配置切面-->
<bean id="myAspect" class="com.dhy.aop.MyAspect"></bean>
<!--配置织入:在配置文件中告诉Spring框架,那些方法(切点)需要进行哪些增强(前置,后置...)-->
<!--要引入AOP命名空间-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面=切点 通知-->
<aop:before method="before" pointcut="execution(public void com.dhy.aop.Target.show())"/>
</aop:aspect>
</aop:config>
</beans>
6.测试代码
先导入spring-test的坐标:
代码语言:javascript复制 <!--引入Spring测试坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
如果目标类有实现接口,那么AOP底层原理即使jdk动态代理,容器中保存的组件是其代理对象,而非本类类型,因此再使用getBean获取实例的时候,如果通过字节码文件类型获取,参数应该填入接口类型,返回值也用接口类型来接
如果目标类没有实现接口,那么AOP底层实现原理就是cglib,我们可以用目标类去接,相当于多态形式
代码语言:javascript复制@RunWith(SpringJUnit4ClassRunner.class)
//在测试类的内部,完成Bean的注入
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test()
{
target.show();
}
}
切点表达式的写法
切点表达式支持的通配符
1.代表匹配一个或者多个字符: Math*r—>Math开头,r结尾的字符串都满足格式要求
2.匹配任意一个参数
3.只能匹配一层路径
代码语言:javascript复制 <aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面=切点 通知-->
<!--cmo.dhy.aop报下的任意类的任意方法的任意参数,返回值任意的方法都会被增强-->
<aop:before method="before" pointcut="execution(* com.dhy.aop.*.*(..))"/>
</aop:aspect>
</aop:config>
通知的类型
通知的配置语法
before,after,aroud演示
切面类:
代码语言:javascript复制public class MyAspect {
public void before()
{
System.out.println("前置增强");
}
public void after()
{
System.out.println("后置增强");
}
//Proceeding JoinPoint: 正在执行的连接点--->切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
//就是利用反射调用的目标方法,等同于method.invoke(obj,args)
Object proceed = pjp.proceed();//切点的方法
System.out.println("环绕后");
//反射调用的返回值也要返回回去,这个返回值就是方法的返回值
return proceed;
}
}
切面的配置:
代码语言:javascript复制 <aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面=切点 通知-->
<aop:before method="before" pointcut="execution(* com.dhy.aop.*.*(..))"/>
<aop:after method="after" pointcut="execution(* com.dhy.aop.*.*(..))"/>
<aop:around method="around" pointcut="execution(* com.dhy.aop.*.*(..))"/>
</aop:aspect>
</aop:config>
结果:
after-throwing
目标类抛出异常:
代码语言:javascript复制public class Target implements TargetInterface{
public void show() {
System.out.println("Target's show");
int i=1/0;//制造数学异常
}
}
切面配置:
代码语言:javascript复制 <aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面=切点 通知-->
<aop:around method="around" pointcut="execution(* com.dhy.aop.*.*(..))"/>
<aop:after-throwing method="throwing" pointcut="execution(* com.dhy.aop.*.*(..))"/>
</aop:aspect>
</aop:config>
增强方法:
代码语言:javascript复制 public void throwing()
{
System.out.println("抛出异常");
}
after—最终增强,不管是否抛出异常,都会执行
增强方法:
代码语言:javascript复制 public void after()
{
System.out.println("最终增强");
}
切面配置:
代码语言:javascript复制 <aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面=切点 通知-->
<aop:around method="around" pointcut="execution(* com.dhy.aop.*.*(..))"/>
<aop:after-throwing method="throwing" pointcut="execution(* com.dhy.aop.*.*(..))"/>
<aop:after method="after" pointcut="execution(* com.dhy.aop.*.*(..))"/>
</aop:aspect>
</aop:config>
切点表达式的抽取
xml配置中也可以在指定前置…方法的时候,通过设置returning和throwing参数,来获取相应的返回值和异常信息
抽取后得到的pointcut标签,如果写在了当前的切面类里面,那么也只能够在当前切面类里面引用,如果想扩大作用域范围,可以将标签写在config表签下面,相当于一个全局变量
可以给aspect标签里面加上order属性,来指定切面的执行顺序
切面配置:
代码语言:javascript复制 <aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--抽取切点表达式-->
<aop:pointcut id="myPointCut" expression="execution(* com.dhy.aop.*.*(..))"/>
<!--切面=切点 通知-->
<aop:around method="around" pointcut-ref="myPointCut"/>
<aop:after-throwing method="throwing" pointcut-ref="myPointCut"/>
<aop:after method="after" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
知识要点
基于注解的AOP开发
快速入门的步骤
别忘记开启基于注解的aop功能—>aop自动代理
配置文件:
代码语言:javascript复制 <!--组件扫描-->
<!--使用时,加上context的命名空间-->
<context:component-scan base-package="com.dhy.anno"/>
<!--aop的自动代理-->
<!--使用时,加上aop的命名空间-->
<aop:aspectj-autoproxy/>
切面类:
代码语言:javascript复制@Component("MyAspect")
@Aspect //标注当前类是一个切面类
public class MyAspect {
//Proceeding JoinPoint: 正在执行的连接点--->切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
Object proceed = pjp.proceed();//切点的方法
System.out.println("环绕后");
return proceed;
}
public void throwing() {
System.out.println("抛出异常");
}
//配置最终增强
@After("execution(* com.dhy.anno.*.*(..))")//参数是切点表达式
public void after()
{
System.out.println("最终增强");
}
}
目标类:
代码语言:javascript复制@Component("Target")
public class Target implements TargetInterface {
public void show() {
System.out.println("Target's show");
}
}
注解通知类型
通知方法的执行顺序
在通知方法运行时,拿到目标方法的详细信息----JoinPoint
我们只需要为通知方法的参数列表上写上一个参数: JoinPoint joinpoint (封装了目标方法的详细信息)
详细看本篇连接
throwing和returning来指定哪个参数用来接收异常和返回值
returning=“result” :告诉spring,使用result变量来接收返回值
throwing=“exception”: 告诉spring,exception变量用来接收异常
spring通知方法的参数列表一定不能乱写
如上面的returning和throwing方式,告诉spring我们填入的参数是什么
上面returning和throwing用来接收异常和返回值信息的指定参数的数据类型,最好往大了写,不然可能无法接收数据
切点表达式的抽取----随便声明一个返回值为void的空方法
切面类:
代码语言:javascript复制@Component("MyAspect")
@Aspect //标注当前类是一个切面类
public class MyAspect {
@Around("point()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
Object proceed = pjp.proceed();
System.out.println("环绕后");
return proceed;
}
@After("MyAspect.point()")
public void after()
{
System.out.println("最终增强");
}
//定义切点表达式
@Pointcut("execution(* com.dhy.anno.*.*(..))")
public void point(){};
}
可以使用环绕通知完成四合一功能: 前置,返回,异常,后置
如果在使用环绕通知时,代码里面对异常进行了抓取,为了能让外界知道这个异常,这个异常一定要抛出去
环绕通知优先于普通通知执行
如果想要通过通知来实现动态代理的功能,那么就可以选择环绕通知,利用里面的ProceedingJoinPoint 类型的对象,完成对方法执行的干预
多切面运行顺序
注解方式是默认按照类名的字符串大小比较来决定执行顺序,如果时xml配置,则是由配置的先后顺序决定 可以使用@order(int i)注解,来改变切面的运行顺序,数值越小,优先级越高
环绕只影响当前切面,他的优先执行顺序只体现在它所在的当前切面,与其他切面无关
AOP的使用场景
Spring的JDBCTemplte
JDBCTemplte开发步骤
1.导入spring-jdbc和spring-tx以及其他的一些坐标
代码语言:javascript复制<!--mysql驱动的坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!--c3p0数据库连接池的坐标-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--druid数据库连接池坐标-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--spring jdbc的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--spring tx的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
创建数据源,设置数据源,执行操作
代码语言:javascript复制public class JdbcTemplteTest {
@Test
public void test1() throws PropertyVetoException {
//创建数据源对象
ComboPooledDataSource dataSource=new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test1");
dataSource.setUser("root");
dataSource.setPassword("126433");
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//设置数据源对象 知道数据库在哪
jdbcTemplate.setDataSource(dataSource);
//执行操作
int row=jdbcTemplate.update("insert into account values (?,?)","大忽悠",8000);
System.out.println("影响的行数:" row);
}
}
通过Spring让我们产生模板对象
通过appOfDao.xml完成配置
将数据库连接池对象注入到jdbctemplate模板对象中
代码语言:javascript复制 <!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test1"/>
<property name="user" value="root"/>
<property name="password" value="126433"/>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
测试类:
代码语言:javascript复制public class JdbcTemplteTest {
@Test
public void test1() throws PropertyVetoException {
ApplicationContext app = new ClassPathXmlApplicationContext("appOfDao.xml");
JdbcTemplate jt=app.getBean(JdbcTemplate.class);
int row = jt.update("insert account values (?,?)", "小朋友", 12000);
System.out.println("影响的行数" row);
}
}
抽取数据库连接池配置时填入的参数,放到properties配置文件中
properties配置文件:
代码语言:javascript复制jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test1
jdbc.username=root
jdbc.password=126433
在Spring容器中引入pro配置文件,然后修改刚才传入的参数—配置数据库的模板
代码语言:javascript复制 <!--加载jdbc.properties-->
<!--引入context命名空间-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>