Spring5
Spring5
1.什么是Spring
-
是一个轻量级(jar包少)的开源的JAVAEE框架(容器)
-
是为了解决企业应用开发的复杂性
-
支持事务的处理,对框架整合的支持
-
两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象的过程交给Spring进行管理
- AOP:面向切面,不修改源代码进行功能增强
-
Spring的特点
- 方便解耦(ioc),简化开发
- Aop编程支持
- 方便程序测试
- 方便和其他框架进行整合
- 方便进行事务操作
- 降低了API的开发难度
官网下载地址:https://repo1.maven.org/maven2/org/springframework/spring/
Github下载地址: GitHub - spring-projects/spring-framework: Spring Framework
Maven(导Spring web Mvc):
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.13</version> </dependency>
-
组成
2.IOC理论推导
原来: 在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。
IOC容器:
IOC解耦过程 : “第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心
IOC底层实现:xml,工厂模式,反射
引入:
-
首先我们的Dao层和service层
-
当我们业务层Service调用Dao层时,会有一个创建Dao实现类的方法,并且有一个get方法去获取
public UserDao userDao=new UserDaoImpl(); //public UserDao userDao=new UserDaoImpl2(); 修改的业务 @Override public void getUser() { System.out.println(userDao.run()); }
-
测试类中
UserService userService = new UserServiceImpl(); userService.getUser();
-
上面的我们就会发现一个弊端,当我们的业务需要修改时,我们必须去修改业务层的代码
-
这时就出来了一种解决办法,我们用一个set方法控制创建哪一种业务的对象
public UserDao userDao; public void setUserDao(UserDao userDao){ this.userDao=userDao; } @Override public void getUser() { System.out.println(userDao.run()); }
-
测试类时,我们可以先调用set方法来选择不同的业务,这就是IOC的原型
@Test public void test(){ UserService userService = new UserServiceImpl(); ((UserServiceImpl)userService).setUserDao(new UserDaoImpl()); userService.getUser(); }
-
我们从主观的去创建对象,变成被动的去接受对象
-
这种思想,从本质上解决了问题,我们程序员不需要去管理对象的创建,程序的耦合性降低,可以更加专注在业务的实现上,这就是IOC的原型
3.IOC本质
- 控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了
- IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
- 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
4.HelloSpring
4.1丶实体类
public class Hello {
private String name;
public void setName(String name) { //注意这里set方法必须有(依赖注入 : 就是利用set方法来进行注入的.)
this.name = name;
}
public void show(){
System.out.println(name+"你好呀!");
}
}
4.2丶写bean配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
原来:Hello hello =new Hello();
现在:bean就代表一个对象,id是对象名,class是映射到对象类的位置(使用完全限定的类名)
property 设置属性和属性值,一个属性就要对象相对的set
-->
<bean id="Hello" class="com.LiuDeHen.pojo.Hello">
<property name="name" value="spring"/>
value=""//具体的值
ref=""//使用Spring容器中创建的对象
</bean>
</beans>
4.3丶测试
Spring提供IOC容器实现的两种方式:(两个接口)
-
BeanFactory:IOC容器的基本实现,Spring内部接口,不推荐使用
- 特点:加载配置文件不创建对象,在获取对象时才去创建对象
-
ApplicationContext:BeanFactory的子接口,提供了更多的方法,推荐
-
特点:加载配置文件就创建对象,我们web应用时,就需要这种开始就创建好的
-
常用的实现类:
-
-
public void test(){
// 获取Spring上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
// getBean获取id=Hello的bean对象
Hello hello = (Hello) context.getBean("Hello");
hello.show();
}
4.4丶总结:
- 上面的过程 就叫控制反转 :
- 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的 .
- 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
- 依赖注入 : 就是利用set方法来进行注入的. (跟上面推导的IOC原型一样)
- IOC是一种编程思想 , 由主动的编程变成被动的接收 .
- 所谓的IOC就是:Spring去创建,管理,装配对象
- Spring提供IOC容器实现的两种方式
5.Bean管理
1.什么是bean管理
- spring创建对象
- spring注入属性
2.Bean管理操作的两种方式
- xml
- 注解
3.基于xml方式创建对象
-
使用bean标签
<bean id="Hello" class="com.LiuDeHen.pojo.Hello">
-
bean标签常用属性
- id:标识
- class:类全路径(包类路径)
-
创建对象的时候,默认也是执行无参构造函数
4.基于xml方式注入属性
-
DI注入:依赖注入,就是注入属性
-
第一种:set方法注入
-
创建类,定义属性和set方法
-
之前,就是new对象,调用set方法,注入
-
现在,xml文件中Bean标签创建对象,属性注入
<bean id="UserServiceImpl" class="com.LiuDeHen.Service.UserServiceImpl"> <!-- name:类里的属性名字 value:属性的值 ref:属性是对象时用 --> <property name="userDao" ref="UserDao1"/> </bean>
-
-
第二种:有参构造注入
-
类中创建有参构造
-
使用子标签
//第一种方式 <bean id="Hello" class="com.LiuDeHen.pojo.Hello" name="我是大傻逼 我是小煞笔,我真是傻逼" > <constructor-arg name="name" value="spring"/> <constructor-arg name="day" value="5"/> <!-- 当传入的值类型是引用类型,可以用ref --> </bean> <!-- 第二种方式 index:表示第几个参数 --> <bean id="Hello" class="com.LiuDeHen.pojo.Hello"> <constructor-arg index="0" value="Spring"/> <constructor-arg index="1" value="5"/> </bean>
-
-
5.p名称注入
6.注入空值和特殊符号
```xml
属性中用null标签实现空值
特殊符号
<value><![CDATA[《南京》]]></value>
</property>
```
7.注入属性--外部Bean,内部Bean和级联赋值
-
当你从service层类调用Dao层类的方法怎么实现
-
外部Bean,ref调用其他对象
-
内部Bean,在bean里的属性里再创建bean
-
级联:跟外部的区别就是,给调用的那个类赋了值
外部: <bean id="Dao" class="com.LiuDeHen.Dao.Arrecess"/> <bean id="service" class="com.LiuDeHen.Dao.Student" > <!-- bean注入,ref --> <property name="Dao" ref="Dao"/> </bean> 内部: <bean id="name" class="com.LiuDeHen.Dao.Student" > <property name="arrecess"> <bean id="a" class="com.LiuDeHen.Dao.Arrecess"> <property name="age" value="21"/> </bean> </property> </bean> 级联:跟外部的区别就是,给调用的那个类赋了值 <bean id="service" class="com.LiuDeHen.Dao.Student" > <!-- bean注入,ref --> <property name="Dao" ref="Dao"/> //第二种方式:需要Dao里有获取另一个对象的get方法 <property name="Dao.age" value="21"/> </bean> //第一种方式 <bean id="Dao" class="com.LiuDeHen.Dao.Arrecess"> <property name="age" value="21"/> </bean>
8.注入集合等
<bean id="jihe" class="com.LiuDeHen.pojo.Hello">
<!--数组,list,set-->
<property name="list" >
<list> <!--set,array -->
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<!--Map-->
<property name="map" >
<map>
<entry key="1" value="1"/>
<entry key="1" value="1"/>
</map>
</property>
<!--当插入对象时-->
<property name="list" >
<list> <!--set,array -->
<ref="a"/>
</list>
</property>
</bean>
<bean id="person" class="com.LiuDeHen.pojo.person">
<property name="age" value="21">
</bean>
注意:
- 当集合中要注入对象时,我们可以先创建对象,再在里面ref引入
9.Spring内置工厂Bean(FactoryBean)
-
spring有两种Bean:
-
我们创建的普通Bean
- 在配置文件中定义Bean类型就是返回的类型
-
内置的工厂Bean
-
在配置文件定义Bean类型可以和返回类型不一样
-
第一步:让某个类作为工厂Bean,实现接口FactoryBean
-
第二步:实现接口里面实现的方法,在实现的方法中定义返回的Bean类型
public class User implements FactoryBean<Hello> { @Override public Hello getObject() throws Exception { Hello hello = new Hello(); return hello; } }
-
-
10.bean的作用域
1.我们使用bean创建的对象是单实例对象还是多实例对象
@Test
public void test(){
// 获取Spring上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
// getBean获取id=Hello的bean对象
Hello hello = (Hello) context.getBean("jihe");
User user1 = hello.getUser();
User user2 = hello.getUser();
System.out.println(user1);
System.out.println(user2);
}
2.单实例对象
3.我们可以通过修改bean的属性scope值来修改是单实例还是多实例
-
singleton:单例模式:一加载配置文件就创建对象 prototype:原型模式:在调用getbean的时候才创建对象
11.Bean生命周期
-
生命周期:
从对象创建到销毁的过程
-
bean的生命周期步骤
- 通过无参构造创建bean实例
- 为bean注入属性值或者其他对象的引用
- 调用bean的初始化方法(需要进行配置初始化的过程)
- bean可以使用了(获取到对象)
- 当容器关闭的时候,调用bean的销毁方法(需要配置销毁的方法)
-
演示
public class bean { public bean(){ System.out.println("第一步通过无参构造创建实例"); } private String name; public void setName(String name) { this.name = name; System.out.println("第二步,调用了set方法"); } public void initMethod(){ System.out.println("第三步执行初始化方法"); } public void destoryMythod(){ System.out.println("第五步执行销毁方法"); } }
init-method destroy-method:将自己定义的方法配置上去 <bean id="beans" class="com.LiuDeHen.pojo.bean" init-method="initMethod" destroy-method="destoryMythod"> <property name="name" value="刘超"/> </bean>
@Test public void test(){ // 获取Spring上下文对象! ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml"); bean beans = context.getBean("beans", bean.class); System.out.println(beans); System.out.println("第四步:调用对象"); // 手动销毁 context.close(); } }
4.后置处理器
-
会在初始化前后,执行
-
创建一个类,实现一个接口BeanPostProcessor
-
具体演示
-
public class myBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行"); return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行"); return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }
-
并在配置文件中创建对象,以后每一个bean就都会执行这个后置处理器
5.总结
- Bean的生命周期一共7步
12.xml自动装配
-
什么是自动装配
- 根据你的属性名称或者属性类型,spring会自动将匹配的属性值注入
-
实例
<!-- autowire:会在spring容器上下文自动匹配 autowire=byname:根据id值到类中匹配来自动装配 autowire="byType:根据class你的类型来装配 --> <!-- <bean id="Human" class="com.LiuDeHen.Dao.Human" autowire="byName">--> <bean id="Human" class="com.LiuDeHen.Dao.Human" autowire="byName"/> <!--当有两个类型一致的时候,就不能用bytype,因为他无法识别是哪一个 --> <bean id="dog" class="com.LiuDeHen.Dao.Dog"/> <bean id="dog2" class="com.LiuDeHen.Dao.Dog"/> <bean id="cat1" class="com.LiuDeHen.Dao.Cat"/> <bean id="cat" class="com.LiuDeHen.Dao.Cat"/>
13.连接数据库
先导包(Druid德鲁伊)
-
直接配置
<!--直接配置连接池--> <bean id="datesource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="password" value="123"/> <property name="url" value="jdbc:mysql//localhost:3306/mybaties"/> <property name="name" value="root"/> </bean>
-
引入外部属性文件配置(properties)
-
先导入约束
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
-
-
配置文件
prop.driverClassName=com.mysql.jdbc.Driver prop.password=123 prop.url=jdbc:mysql//localhost:3306/mybaties prop.name=root
- 引入
```xml
<!-- 引入 -->
<context:property-placeholder location="classpath:1.properties"/>
<!--直接配置连接池-->
<bean id="datesource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClassName}"/>
<property name="password" value="${prop.password}"/>
<property name="url" value="${prop.url}"/>
<property name="name" value="${prop.name}"/>
</bean>
14.Bean创建对象(基于注解)
常用注解:
Spring---基于注解开发 - SegmentFault 思否
使用:
Spring5:@Autowired注解、@Resource注解和@Service注解 - 五月的仓颉 - 博客园 (cnblogs.com)
-
什么是注解
- 注解是代码的特殊标记,格式:@注解名(属性名称=值,属性名称2=值2)
- 可以加在类,方法,属性上
- 目的:简化xml配置
-
Spring针对Bean管理中创建对象提供的注解
- @Component
- @Service:业务层
- @Controller:控制层
- @Repository:Dao层
- 功能一样,都是用来创建Bean实例
-
基于注解方式实现对象的创建
-
第一步,引入依赖
-
第二步,开启组件扫描(需要开启context依赖)
<!-- 扫描 当扫描多个包,可以用逗号隔开 --> <context:component-scan base-package="com.LiuDeHen.Dao"/>
-
第三步,使用注解,创建对象实例
//value属性值可以省略不写 //默认就是类名首字母小写 //Cat----cat @Component(value="cat") //相当于<bean id="cat" class="扫描"> public class Cat { public void shout(){ System.out.println("猫在叫"); } }
-
测试
public class myTest { @Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //这个cat就是注解后的value值 Cat cat = applicationContext.getBean("cat", Cat.class); cat.shout(); } }
-
15.Bean注入属性(注解)
-
@Autowired:根据类型进行自动装配
-
首先必须得创建这个对象
-
再使用注解找到这个类型自动装配
@Repository public class UserDao { public String run(){ return "UserDao"; } }
@Repository public class UserService { // 不需要set方法(内部帮我们解决了) @Autowired UserDao userDao; public void run(){ System.out.println("service"); System.out.println(userDao.run()); } }
-
-
@Qualifier:根据属性名称进行注入
-
跟@Autowired一起使用,因为Autowired是跟据类型,可能有类型相同时,就需要这个注解
@Autowired @Qualifier("Dog") UserDao userDao;
-
-
@Resource:可以根据类型,也可以根据名称注入 (不是spring的包,是java的扩展包的注解)
// @Resource @Resource(name="userDao") UserDao userDao;
-
@Value:注入普通属性
@Repository public class UserDao { @Value(value="你好") private String name; public String run(){ return name; } }
16.完全注解开发(Springboot常用)
-
创建配置类,代替xml文件
@Configuration //相当于xml文件 @ComponentScan(basePackages = {"com.LiuDeHen"}) //相当于扫描 public class more { }
-
测试类:AnnotationConfigApplicationContext
@Test
public void test() {
ApplicationContext context = new AnnotationConfigApplicationContext(more.class);
UserService bean = context.getBean("userService", UserService.class);
bean.run();
}
6.阶段总结
1.什么是spring
- spring是一个开源轻量级框架
- 为了简化企业开发
2.spring核心
- **IOC容器**
- **AOP切面编程**
3.IOC容器的推导以及意义
- 原本
- 在业务层创建Dao层的对象
- 每使用一个对象创建一次
- IOC容器的由来
- 在业务层创建一个set方法,参数为Dao的接口,返回一个Dao对象(多态)
- 在我们的测试类中,不同的对象只需要设置set里的Dao对象即可
- 意义:**由主动的创建对象,变成我们可以被动的管理对象**,降低耦合性
- **IOC:控制反转**
4.怎么用spring使用IOC容器
4.1丶IOC是一种容器(思想),DI是注入(手段)
- set注入:
- 首先必须要有这个set方法
- 再在property属性中就可以了
- 构造器注入
- 首先要有相对应的构造器,默认无参
- 使用构造器的标签constructor-arg
- 有使用对应参数名(推荐),参数下标,参数类型多种匹配方式
- bean作用域:
- 属性:scope
- 有单例,所有对象共用:singleton
- 原型:单对象独享:prototype
4.2丶使用xml的方式创建
- 创建实体类
- 创建bean.xml文件,导入spring容器配置
- 设置xml文件中的bean,id为唯一标识,class为类的路径,一个bean为一个对象
- bean中标签property为属性
- 基础类型:name为属性名,value为值
- 其他类型,list就是又list标签,array就是array标签等
- 测试类创建spring容器上下文对象 new ClassPathXmlApplicationContext("xml文件")
4.3丶自动装配(隐式创建):
- xml和使用java都是显示创建
- 常用有:byName,ByType
- autowire=byname:根据id值来自动装配
- autowire="byType:根据class你的类型来装配
4.4丶使用注解创建
- 在xml文件中
```xml
<!--扫描包 -->
<context:component-scan base-package="com.LiuDeHen"/>
其他的bean都
```
- 在对应类上使用注解**@Component**,就可以**创建bean**对象
- 使用@value:给属性赋值
- 自动装配@Autowired
4.5丶使用全java的方式(在springboot很常用)
- 创建一个实体类
- 创建一个配置类,有一个get方法,返回的就是实体类的对象
- get方法上有一个注解**@Bean**,**创建**一个bean对象,类上有一个注解**@Configurable**实现自动装配
- 这个类就是之前的xml文件,每一个get方法就是一个xml里的bean对象,方法名是id,返回值是class
7.代理模式
什么是代理模式?
- 实际的例子:我们在登录,注册的时候,验证码是手机短信,不是我们发送,而是移动,电信等公司面向社会提供发送短信的功能
- 开发中的例子有a,b(代理类),c(目标类)类,a类不能直接(或者不适合)调用c类,但是a类能调用b类,b类能调用c类
为什么要学代理模式?
- AOP底层就是代理模式
- 功能增强:在原有的功能上,新增功能
- 控制访问:代理类不让你访问目标
代理模式的分类
- 静态代理
- 动态代理
12.1丶静态代理
角色分析
- 抽象角色:一般使用接口和抽象类,在这个实例里就是一个出租房接口
- 真实角色:被代理的角色,这里是房东
- 代理角色:代理真实角色,代理角色后,会有一些其他操作,这里是中介
- 客户:访问代理角色的人,这里是客户
抽象角色:
//买卖房接口
public interface Rent {
void rent();
}
真实角色:
//房东类
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东卖房");
}
}
代理角色:
//代理类
public class proxy implements Rent{
private Host H;
public proxy(Host host){
H=host;
}
//继承的父类方法
@Override
public void rent() {
H.rent();
}
//自己的方法
public void sou(){
System.out.println("收取中介费");
}
}
客户:
//客户类
public class client{
public static void main(String[] args) {
proxy proxy = new proxy(new Host());
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给了代理角色,实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理且不修改原来的代码
缺点:
- 一个真实角色就会产生一个代理角色,代码量翻倍,效率变低
12.2丶加深理解
AOP:面向切面编程,横向开发,再不修改代码的情况下,增加业务
12.3丶动态代理
- 动态代理角色和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口:----JDK动态代理(我们在这里使用,反射)
- 什么是动态代理?
- 使用jdk的反射机制,创建对象的能力,创建的是代理类的对象
- 动态:在程序执行的过程中,调用jdk提供的方法才能创建代理类的对象
- 使用jdk反射包中的三个类和接口实现动态代理的功能
- 反射包java.lang.reflect三个类:InvocationHandler,Method,Proxy
- InvocationHandler接口就实现一个方法:invoke(),代理类实现的功能就写在里面
- Method类:Method.invoke()执行目标方法
- Proxy类:核心对象,创建代理对象
- 静态方法:newProxyInstance()创建对象
- 什么是动态代理?
- 基于类:cglib代理
- java字节码实现:javasist
- 基于接口:----JDK动态代理(我们在这里使用,反射)
实现动态代理的步骤
- 创建接口,定义目标类要完成的功能
- 创建目标类
- 创建InvocationHandler接口的实现类,在Invoke()方法中代理类的功能,并返回代理类
- 调用目标方法
- 增强功能
- 使用Proxy类的静态方法,创建代理对象,并把返回值转为接口类型
卖房接口类
public interface Rent {
public void rent();
}
房东类
public class Host implements Rent{
@Override
public void rent() {
System.out.println("我是房东我要卖房");
}
}
代理类(动态代理就是控制这里的res,会根据传入的接口和参数返回是哪一个类,从而实现动态)
//1.实现接口InvocationHandler,重写invoke方法实现代理类要做的功能
public class proxy implements InvocationHandler {
//2.创建目标类实现的接口
private Rent rent;
proxy(Rent rent){
this.rent=rent;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res=null;
// 三个参数,proxy:代理类,method:代理类实现的方法,args:参数
res=method.invoke(rent,args);
// 3.可以增加一些额外方法
if (res!=null){
}
// 返回这个类
return res;
}
}
客户类
public class client{
public static void main(String[] args) {
// 1.创建目标对象
Rent r = new Host();
// 2.自己写的实现InvocationHandle接口的类
InvocationHandler handler = new proxy(r);
// 3.创建代理对象,并转成接口
Rent o = (Rent)Proxy.newProxyInstance(r.getClass().getClassLoader(),
r.getClass().getInterfaces(), handler);
// 4.通过代理执行方法
o.rent();
}
好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共也就交给了代理角色,实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理且不修改原来的代码
- 一个接口代表的是一类业务,修改功能只需要修改极少代码
8.AOP
1.什么是AOP
-
面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,降低耦合度,提高程序可重用性,提高开发效率
-
通俗描述:不修改源代码,就可以在主干功能里添加功能
2.丶AOP术语:
-
连接点(JointPoint) :类里面的方法哪些可以被增强,就是连接点
-
切入点(PointCut) ︰实际被真正增强的方法
-
通知(Advice) :
- 实际增强的逻辑部分称为通知(增强)
- 通知有多种类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知 finally(不管怎么样都会执行)
-
切面(ASPECT)︰把通知应用到切入点的过程
3丶Aop操作准备
- Spring框架一般都是基于Aspectj实现AOP操作的
- Aspectj不是Spring的组成部分,是独立的AOP框架,一般把Aspectj和Spring一起使用
- 基于Aspectj的Aop操作
- XMl配置文件实现
- 注解实现
- 导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
- 切入点表达式
- 作用:知道是对哪个类的哪个方法进行增强
- 语法结构:execution(【权限修饰符】【返回类型】【类全路径【方法名称】【参数列表】)
- 举例
- 对com.LiuDeHen.dao.Book的所有方法进行增强
- execution(* com.LiuDeHen.dao.Book.*(..))
- *表示所有
- 返回值类型可省略
- (..)表示参数列表
- 对com.LiuDeHen.dao.Book的所有方法进行增强
4.AOP操作(Aspectj注解)
-
导入依赖和导入约束,导入约束时下面两个就修改最后那一点,比如beans改为aop
-
导入依赖时遇到的问题
-
导入aspectjweaver包
-
不要导aspect包,不然会报异常
nested exception is java.lang.NoClassDefFoundError:org/aspectj/weaver/reflect/ReflectionWorld$Refle
<dependency> <groupId> org.aspectj</groupId > <artifactId> aspectjweaver</artifactId > <version> 1.6.11</version > </dependency>
-
-
-
创建普通类和代理类(@Aspect)
//普通类 @Component public class User { public void run(){ System.out.println("你好"); } }
//创建代理类 @Component @Aspect //配置切面 //@EnableAspectJAutoProxy(proxyTargetClass = true)跟xml文件里自动找到代理类一样的作用 //默认false即jdk代理,true即cglib代理,即使默认false,被代理对象没有接口也会采用cglib public class UserProxy { @Before("execution(* com.LiuDeHen.Dao.User.run(..))") public void before(){ System.out.println("Before通知"); } // 最终通知(有异常也执行) @After("execution(* com.LiuDeHen.Dao.User.run(..))") public void After(){ System.out.println("After通知"); } // 后置通知(返回通知,有异常不执行) @AfterReturning("execution(* com.LiuDeHen.Dao.User.run(..))") public void AfterReturning(){ System.out.println("AfterReturning通知"); } // 异常通知 @AfterThrowing("execution(* com.LiuDeHen.Dao.User.run(..))") public void AfterThrowing(){ System.out.println("AfterThrowing通知"); } // 环绕通知 @Around("execution(* com.LiuDeHen.Dao.User.run(..))") public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕通知前"); // 增强的方法 proceedingJoinPoint.proceed(); System.out.println("环绕通知后"); } }
-
开启包扫描创建bean和自动创建代理类
<!--扫描包 --> <context:component-scan base-package="com.LiuDeHen.Dao"/> <!-- 会自动找到代理类 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
测试
@Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User bean = context.getBean("user", User.class); bean.run(); }
-
抽取相同切入点
//提取切入点 @Pointcut(value = "execution(* com.LiuDeHen.Dao.User.run(..))") public void point(){ } // 直接调方法 @Before(value = "point()") public void before(){ System.out.println("Before通知"); }
-
当有多个增强类,对同一个方法进行增强,使用注解@Order(1),值越小,优先级越高
@Component @Aspect @Order(1) public class UserProxy2 { // 最终通知(有异常也执行) @After("execution(* com.LiuDeHen.Dao.User.run(..))") public void After(){ System.out.println("After2通知"); } }
5.AOP操作(Aspectj配置文件)
- 创建普通类和增强类,及方法
- 在spring配置文件中创建对象
- 在spring配置文件中配置切入点
<!--创建对象-->
<bean id="person" class="com.LiuDeHen.service.Person"/>
<bean id="personProxy" class="com.LiuDeHen.service.PersonProxy"/>
<aop:config>
<!-- 设置切入点,切到哪个方法 -->
<aop:pointcut id="p" expression="execution(* com.LiuDeHen.service.Person.run(..))"/>
<!-- 设置切面,也就是代理类 -->
<aop:aspect id="" ref="personProxy">
<!-- a是代理类里的增强方法,p是被代理类的被切入方法 -->
<aop:before method="a" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
方式二:自定义类实现
- 一个切入点类
- 一个额外类,使用原生就是一个额外方法一个类,然后注册,自定义就是所有额外方法一个类
- 注册bean
- 使用aop标签,exection表达式
- execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。
<bean id="diy" class="com.LiuDeHen.diy.divPointcut"/>
<aop:config>
<!-- 自定义切面 ref:要引入的类 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.LiuDeHen.service.userServiceImpl.*(..))"/>
<!-- 通知-->
<aop:after method="After" pointcut-ref="point"/>
<aop:before method="Before" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:使用注解实现AOP
无非就是
- 设置切入面@Aspect
- 设置需要切入的方法是前是后还是环绕@After,@Before,@Around
- 设置切入点(用exection表达式):execution(* com.LiuDeHen.service.userServiceImpl.*(..))
//注册bean(使用前,需要bean里必须有扫描包)
@Component
//开启注解支持,有参数设置为false就是默认为jdk代理,ture为cglie代理
@EnableAspectJAutoProxy
//设置切入面
@Aspect
public class divPointcut {
// 设置切入位置和execution表达式(切入点的位置)
@After("execution(* com.LiuDeHen.service.userServiceImpl.*(..))")
public void After(){
System.out.println("方法执行前");
}
@Before("execution(* com.LiuDeHen.service.userServiceImpl.*(..))")
public void Before(){
System.out.println("方法执行后 ");
}
// 在环绕增强中,给定一个参数ProceedingJoinPoint,表示我们要获取处理切入的点
@Around("execution(* com.LiuDeHen.service.userServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
9丶整合Mybatis
-
导入jar包
- junit
- mybatis
- mysql
- spring
- aop织入
- mybatis-spring
-
配置环境
-
导入spring配置
-
Datesource代替mybatis的数据源,需要类org.springframework.jdbc.datasource.DriverManagerDataSource
-
SqlSessionFactory获取工厂,并可以设置mybatis里的配置(mapper位置等)需要类org.mybatis.spring.SqlSessionFactoryBean
-
SqlSessionTemplate相当于获取了SqlSession,需要类org.mybatis.spring.SqlSessionTemplate
-
Dao接口多了实现类
<!-- 2.Datesource:使用spring的数据源替换mybatis的配置 这里我们使用spring提供的org.springframework.jdbc.datasource --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybaties?useSSL=false&;characterEncoding=UTF-8&;useUnicode=true&;serverTimezone=GMT"/> <property name="username" value="root"/> <property name="password" value="123"/> </bean> <!--3.SqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 配置的位置 --> <property name="configLocation" value="classpath:mybatisSpring.xml"/> <!-- Mapper的位置 --> <property name="mapperLocations" value="classpath:com/LiuDeHen/Dao/UserMapper.xml"/> </bean> <!-- 4.SqlSessionTemplate:就是我们使用的Sqlsession --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入SqlSessionTemplate,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> <!-- 5.实现类 --> <bean id="UserMapperImpl" class="com.LiuDeHen.Dao.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
实现类:第一种方式SqlSessionTemplate public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } @Override public List<User> select() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.select(); } } 第二种方式:SqlSessionDaoSupport 继承SqlSessionDaoSupport,获取SqlSessionTemplate,并且此时bean里5的实现类属性变成sqlSessionFactory public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{ @Override public List<User> select() { UserMapperImpl mapper = getSqlSession().getMapper(UserMapperImpl.class); return mapper.select(); } }
-
-
测试
-
直接获取实现类的bean对象
@Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); UserMapperImpl userMapperImpl = applicationContext.getBean("UserMapperImpl", UserMapperImpl.class); for (User user : userMapperImpl.select()) { System.out.println(user); }
-
10.声明式事务
1.回顾事务
- dml业务要么都成功,要么都失败
- 典型案例:
- 银行转账:luck转账给mary100元,
- luck少100,mary多100
- 如果中间出现问题,那么luck不能少,mary不能多
- 确保完整性和一致性
事务的ACID原则(原一隔持):
- 原子性:确保dml业务,要么都成功,要么都失败
- 一致性:一旦事务完成,要么都提交,要么失败,资源和状态一直保持一致
- 隔离性:多个事务操作一个数据,防止数据损坏
- 持久性: 事务一旦提交,无论系统发生什么问题,结果都不会受到影响,被持久化写到硬盘
2.使用声明创建事务
-
配置声明事务
<!--配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /> </bean>
-
配置事务的通知
<!--结合AOP实现事务的织入--> <!-- 配置事务的通知--> <tx:advice id="interceptor" transaction-manager="transactionManager"> <!-- 为哪一个方法创建事务 --> <!-- 配置事务的传播特性: propagation=使用 REQUIRED创建,一共有7种 --> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
-
配置事务的切入
<!-- 配置事务的切入 --> <aop:config > <!-- 设置切入点--> <aop:pointcut id="pointcut" expression="execution(* com.LiuDeHen.Dao.UserMapperImpl.*(..))"/> <aop:advisor advice-ref="interceptor" pointcut-ref="pointcut"/> </aop:config>
-
实体类中实现多组dml操作
@Override public int delete(User user) { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); mapper.add(user); mapper.select(); return mapper.delete(user); }
-
当Mapper中的dml操作出现问题
<select id="select" resultType="User"> select * from mybaties.user </select> <insert id="add" parameterType="User"> insert into mybaties.user values (#{id},#{name},#{pwd}); </insert> <delete id="delete" parameterType="User"> 这个语句delete加了s,造成错误 deletes from mybaties.user where id=#{id} </delete>
-
测试就会失败,全部失败,而不是部分失败,没有问题则会全部成功
11.总结
-
什么是spring
- 是一个整合了很多技术,核心点为控制反转(IOC)和面向切面编程(AOP)的轻量级框架
-
spring核心
- IOC
- 什么是IOC?
- IOC为控制反转,是一种思想,由主动的创建对象,到被动管理对象,创建等操作交给spring容器来实现
- 基础实现:
- 1.当我们数据操作层Dao的接口有多个方法,不同的实现类继承这个接口
- 2.那么在我们测试的时候,每测试一个实现类,都需要去实例化,主动权在我们程序员手里,那么怎么实现IOC呢
- 3.添加一个业务层Service,实现接口和实体类,并在实体类中,去set和get一个Dao层的接口
- 4.此时我们测试,只需要new业务层,去调用set来实现Dao层哪一个具体的实现类的方法
- 5.好处:此时主动权在用户手上,这就是控制反转
- AOP
- 什么是AOP?
- 面向切面编程,底层是动态代理
- 什么是代理?
- 1.首先有一共目标类,客户类,客户类不能直接调用或者不适用目标类,此时就需要一共代理类
- 2.实例:租客,通过中介找房,中介找房东
- 3.好处:中介可以使用额外的方法,并且不影响到房东和租客(代理类能够在不改变目标类和客户类的情况下,能够添加方法,控制访问)
- 4.动态代理是AOP的底层
- 代理分类
- 静态代理
- 动态代理
- 默认JDK代理必须通过接口实现
- IOC
-
spring怎么用(IOC)
- 第一种方式:bean创建
- 先创建实体类
- 构建bean.xml文件,创建bean对象
- 测试调用这个对象
- 第二种方式:bean扫描,在java类中创建
- 实体类
- bean里扫描包
- 实体类上实现Component创建beans,就是一共对象,然后还可以用value注入值,Autowire自动匹配
- 第三种:纯java创建
- 实体类
- 然后容器类实现注解confige(容器)和bean(对象)
- 测试
- 第一种方式:bean创建
-
springAOP的使用
- 基础概念
- 切面
- 切入点
- 切入位置
- 具体使用:
- 第一种方式:原生springAPI的使用
- 导依赖aop的织入,和aop的约束
- 创建aop标签,设置切面,切点
- 第二种方式:
- 第三种方式:
- 第一种方式:原生springAPI的使用
- 基础概念
-
spring-mybatis的连用
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 上周热点回顾(2.17-2.23)
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章