Spring官方中文文档:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/
1,spring(一个抽象的概念)
1.1 spring的maven地址
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!--下面这个包是用来和mybatis整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.22</version>
</dependency>
maven仓库地址:https://mvnrepository.com/
2,IOC思想
让对象可以动态的变化,不写死。
通过一个set方法去给接口赋值
3,Spring项目搭建步骤
第一步:maven项目导入jar包
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring项目第一步导包 -->
<!--导入webmvc的包,可以自动的将一些其他spring包导入-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<!--下面这个包是用来和mybatis整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.22</version>
</dependency>
</dependencies>
第二步;创建xml文件(用来实例化对象)(对象==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">
<!--上面部分不变-->
<!-- 使用Spring来创建对象,在spring这些都称为Bean
java中 类型 变量名 = new 类型();
Hello hello = new Hello();
spring中
id = 变量名
class = new 的对象
property相当于给对象中的属性设置一个值!
-->
<bean id="userbean" class="pojo.User"/>
<bean id="hello" class="pojo.HelloSpring">//创建对象并实例化
<property name="name" value="Spring"/>//给对象属性赋值
<!--
如果属性字段是复合型(对象)则不用value,用ref=“另一个对象的bean ID”
-->
<!--ref:引用spring容器(这个xml文件)中创建好的对象 value: 基本数据类型的值 -->
<property name="user" ref="userbean"/>
</bean>
</beans>
第三步:测试(运行)
//获取Spring的上下文对象
//这句话是固定的,可变的是变量名和参数名,参数名就是需要的(容器)xml文件,可以传多个
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中管理,我们要使用,直接去里面取出来就可以
Hello hello = (Hello)applicationContext.getBean("hello");
//getBean(String str):这个方法是在beans.xml中取对象,参数为对象的id
思考:
- Hello对象是谁创建的? hello对象是由Spring创建的
- Hello对象的属性是怎么设置的? hello对象的属性是由Spring容器设置的 【这个过程叫控制反转】
控制: 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring 后 , 对象是由Spring来创建的
反转:程序本身不创建对象 , 而变成被动的接收对象
4,ioc(Spring)创建对象的方式:
**1,spring核心:get/set中的set方法,没有set方法无法注入 **
2,正常创建对象是使用的无参构造方法创建对象
<bean id="userbean" class="pojo.User"/>
<!--如果类没有无参构造方法,则无法创建对象和实例化(会报错)-->
3,使用有参构造方法创建对象的方式:
<!--方式一:下标赋值-->
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg index="0" value="caishan"/>
<!--index:有参构造方法中,参数的索引位置 value:给这个参数赋的值-->
</bean>
<!--方式二:类型赋值 不建议使用(因为如果多个参数类型一致,则赋值会出错) -->
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg type="java.lang.String" value="caishan"/>
<!--根据有参构造方法的参数类型去赋值,String类的type是java.lang.String,其他就正常 (类这些除外)-->
</bean>
<!--方式三:直接参数名赋值 和property一样的用法,引用对象用ref。只是改了标签名-->
<bean id="user" class="com.kuang.pojo.User">
<constructor-arg name="name" value="caishan"/>
</bean>
总结:
1,spring创建对象,是在配置文件中bean时就被初始化。
2,如果有有参构造方法,则属性通过property标签进行赋值即可。如果没有有参构造方法(代码中只有有参构造方法)贼需要通过constructor-arg标签对属性进行赋值。
5,spring配置
5.1 alias标签(别名)
<!--通过别名获取对象,id叫user的bean 可以用abc来代替-->
<alias name="user" alias="abc"/>
5.2 Bean标签的配置
<!--
id: bean的唯一标识,对象名
class: bean对象所对应的全限类名: 包名+类名
name: 也是别名,而且name同时可以取多个别名,中间用空格,逗号分开都可以(比alias更高级)
-->
<bean id="user" class="com.kuang.pojo.User" name="ab abc">
<constructor-arg name="name" value="ppj"/>
</bean>
5.3 import标签
一般用于团队开发,可以将多个配置文件,导入合并为一个
多人开发,不同的类需要注册到不同的bean中,可以利用import将所有的beans.xml合并
applicationContext.xml
(类似继承)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
//导入了三个beans.xml容器
//使用时直接利用总的xml文件,就可以使用全部的
</beans>
6,DI依赖注入
- 依赖注入: set注入(重点)
- 依赖: bean对象的创建依赖于容器
- 注入: bean对象中的所有属性,由容器来注入
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is 。。。。
不同数据类型的set注入方式:
<?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">
<!--上面部分不变-->
<bean id="adderss" class="pojo.Address">
<property name="address" value="重庆"/>
</bean>
<!---->
<bean id="Student01" class="pojo.Student">
<!--普通数据类型,直接使用value注入-->
<property name="name" value="蔡闪"/>
<!--其他类数据类型,用ref注入-->
<property name="address" ref="adderss"/>
<!--数组类型,在property标签体内使用array标签注入-->
<property name="books">
<array>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</array>
</property>
<!--list集合类型,在property标签体内使用list标签注入-->
<property name="hobbys">
<list>
<value>111</value>
<value>222</value>
<value>333</value>
</list>
</property>
<!--map集合类型,在property标签体内使用map标签注入-->
<property name="card">
<map>
<entry key="ID" value="11223344"/>
</map>
</property>
<!--set集合类型,在property标签体内使用set标签注入-->
<property name="games">
<set>
<value>qqqq</value>
</set>
</property>
<!--null类型,在property标签体内使用null标签注入-->
<property name="wife">
<null/>
</property>
<!--Properties类型,在property标签体内使用props标签注入(有点像结构体)-->
<property name="info">
<props>
<prop key="姓名">111</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
几个注意点:
1,如果数据类型是值,则用value 。如果是对象之类的用ref
2,不变的是 这个部分,基本上都可以放在标签体内部,只是普通的用起来方便
3,除了基础的直接在标签内部赋值外,其他的基本上都有自己的标签体。
4,注意map集合,和Properties类型注入的特点
p标签,c标签注入
1,使用前,需要在头文件前加入两句话
xmlns:p="http://www.springframework.org/schema/p"<!--使用p标签时导入,等价于property -->
xmlns:c="http://www.springframework.org/schema/c"<!--使用c标签时导入,等价于constructor-arg
-->
2,用法
<!-- p命名空间注入,直接在标签体内部进行赋值,就相当于格式化-->
<!--p:name="caishan" 等价于 <property name="name" value="caishan "/> -->
<bean id="user" class="com.kuang.pojo.User" p:name="ppj" p:age="18"/>
<!-- c命名空间注入-->
<bean id="user2" class="com.kuang.pojo.User" c:name="牛天成" c:age="18"/>
3,Bean的作用域
1,单例模式(Spring默认机制)(scope=“singleton”)(只有一个对象)
<bean id="user2" class="com.kuang.pojo.User" c:name="牛天成" c:age="18" scope="singleton"/>
2,原型模式(每次从容器中get的时候,都会产生一个新对象)(scope=“prototype”)
<bean id="user2" class="com.kuang.pojo.User" c:name="牛天成" c:age="18" scope="prototype"/>
3,其余的request,session,application这些个只能在web开发中使用到
7,Bean的自动装配
意义:可以不需要手动的给属性赋值(一般用于有引用型属性)
autowire字段选择装配方式
主要用于属性是对象类型的时候
7,1 使用byName自动装配(autowire=“byName”)
<!--
byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean的id属性
例:在该类中有一个People类的属性和setPeople的方法,那么xml中有一个id为people(忽略大小写)的bean,那么这个属性就自动和id为people的bean绑定。
-->
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
<property name="name" value="ppj"/>
</bean>
<!--byName的时候,需要保证所有bean的id唯一-->
7,2 使用byType自动装配(autowire=“byType”)
<!--
byType: 会自动在容器上下文中查找, 和自己对象属性类型相同的bean
例:在该类中有一个People类的属性,那么xml中有一个类型为People类的bean,那么这个属性就自动和class为People的bean绑定。
-->
<bean id="people" class="com.kuang.pojo.People" autowire="byType">
<property name="name" value="ppj"/>
</bean>
<!--byType的时候,需要保证所有bean的class唯一,使用byType可以不用写id-->
7,3 使用注解自动装配
使用注解装配,需要先导入context约束,context依赖
<!--在头文件加入-->
xmlns:context="http://www.springframework.org/schema/context" <!--**-->
http://www.springframework.org/schema/context <!--**-->
https://www.springframework.org/schema/context/spring-context.xsd <!--**-->
<context:annotation-config/>//导入自动装配的依赖
</beans>
提前也要在spring中注册
除非自动装配的类也用注解进行了注册
注解加入的位置:
//可以加在属性的头上(注意:每一个属性都自己加自己的)
@Autowired(required = false)//如果定义@Autowired(required = false),说明这个对象可以为null,否则不允许为空(默认是true)
private Cat cat;
@Autowired//也可以不加后面的约束
private Cat cat;
//也可以直接加在set方法头上
//也可以忽略set方法,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName默认是通过byType (就是说如果用注解自动装配可以不写set方法但是要在spring中配置,否则不用配置)
如果有多个同类的对象,则无法进行自动的绑定需要使用**@Qualifier(value = “绑定对象的ID名字”)**注解进行配套使用
<!--这是另一个配置注解,可以从byName,byType两种方式去匹配-->
@Resource
private Cat cat;
@Nullable 属性标记了这个注解,说明这个字段可以为null
小结:
@Resource和@Autowired的区别:
都是用来自动装配的,都可以放在属性字段上
@Autowired通过byType的方式实现,而且必须要求这个对象存在 【常用】
@Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到,就会报错
不同:@Autowired通过byType的方式实现 @Resource默认通过byName的方式实现
8,注解开发
使用注解开发的第一步还是需要先导入context约束,依赖
<!--在头文件加入-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" <!--**-->
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context <!--**-->
https://www.springframework.org/schema/context/spring-context.xsd"> <!--**-->
<!-- 区别是指定要扫描的包,包下所以类的注解就会生效-->
<context:component-scan base-package="包的全路径"/><扫描包>
// <context:annotation-config/>//导入的依赖
</beans>
2,需要在指定包的每一个类上加上这个注解**@Component** :意思是该包的对象就在spring容器中被创建了。
该对象的名字为类名的首字母小写形式,也可以在里面写上想要的名字
@Component("注册名") //等价于 <bean id="user" class="pojo.User">
public class User {
<!--需要给对象的属性赋值,则使用@Value("值")注解-->
//等价于 <property name="name" value="ppj"/>
@Value("ppj")
private String name;
}
3, 衍生的注解
@Componet有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
dao [@Repository]
service [@Service]
controller 【@Controller】
这4个注解的功能都是一样的(都等价于@Componet),都是代表将某个类注册到Spring中,装配Bean
4,自动装配注解
@Resource
@Autowired
5,作用域注解
@Scope(“prototype”) [代表该类是单例模式]
6,总结
xml与注解
- xml更加万能,适用于任何场合维护简单方便
- 注解不是自己类使用不了 ,维护相对复杂!
最佳实践:
- xml用来管理bean
- 注解只负责完成属性的注入
- 在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持,开启包扫描
9,使用java的方式配置spring
其中一个方法就是一个bean对象
//@Configuration代表这是一个配置类,就和我们之前看的beans.xml是一样的
@Configuration
@ComponentScan("包名")//扫描包。等价于 <context:component-scan base-package="包的全路径"/>
@Import(配置类名.class)//类似将多个xml文件导成一个
public class MyConfig {
//注册一个bean,就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性(对象名)
//这个方法的返回值,就相当于bean标签中的class属性(类名)
@Bean //等价于<bean id="getUser" class="com.XXX.User"/>
public User getUser(){
return new User(); //就是返回要注入到bean的对象
}
}
//第二个配置类
@Configuration
public class MyConfig2 {
}
调用时:获取池子的方法有所改变
如果完全使用了配置类方式去做,我们就只能通过AnnotationConfigApplicationContext来获取容器,括号里面写这个配置类的类名.class
public class MyTest {
@Test
public void test(){
//如果完全使用了配置类方式去做,我们就只能通过AnnotationConfigApplicationContext来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(类名.class);
User user = context.getBean("getUser", User.class);
System.out.println(user);
}
}
这种纯java的配置方式,在springboot中随处可见!
10,代理模式
注意:一个代理只能对应一个接口
10,1静态代理模式:
代理模式的意义是:为了不改动原有代码【满足开放封闭-原则】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQ18q3qK-1668096073684)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729125335472.png)]
角色分析:
- 抽象角色: 一般会使用接口或者抽象类类解决(房子)(接口类)
- 真实角色:被代理的角色(房东)(实现类)
- 代理角色: 代理真实角色,代理真实角色后,我们一般会做一些附属操作(代理)(调实现类的方法)
- 客户:访问代理对象的人(租客)
代码步骤:
抽象角色:
//房子
public interface Room {
//租房
public void Rent();
}
真实角色:
//房东
public class Host implements Room {
@Override
public void Rent() {
System.out.println("我要出租房子");
}
}
代理角色:(继承接口)
//代理
public class Proxy {
private Host host;
public Proxy() {
}
//客户找代理时,指定需要哪一个房东的房子
public Proxy(Host host) {
this.host = host;
}
public void Rent() {
//租房
host.Rent();
Money();
}
//代理负责收租
private void Money(){
System.out.println("代理收租");
}
}
租客:
//租客
public class Me {
public static void main(String[] args) {
Host host=new Host();
//找到代理,告诉他我要租谁的房子
Proxy proxy=new Proxy(host);
//进行租房
proxy.Rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务也就交给代理角色,实现业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍===开发效率变低(静态代理只允许,一个房东一个代理)
10,2案例二
业务接口:(接口)
public interface UserService {
/*
需求: 在不修改原代码的情况下,添加日志打印信息
*/
public void add();
public void delete();
public void update();
public void query();
}
真实业务:(实现类)
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add方法");
}
@Override
public void delete() {
System.out.println("delete方法");
}
@Override
public void update() {
System.out.println("update方法");
}
@Override
public void query() {
System.out.println("query方法");
}
}
需求:在不改变原有代码的基础上,实现日志打印,借助代理
public class UserServiceProxy implements UserService{
@Override
public void add() {
log("add");
System.out.println("add===");
}
@Override
public void delete() {
log("delete");
System.out.println("delete===");
}
@Override
public void update() {
log("update");
System.out.println("update=====");
}
@Override
public void query() {
log("query");
System.out.println("query=====");
}
public void log(String msg){
System.out.println("[DEBUG]"+msg+"方法执行");
}
}
测试:
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.add();
}
直接用代理对象实现同样的业务接口,打印日志信息,而不触及到原代码
10,3动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:基于接口的动态代理,基于类的动态代理
基于接口----JDK动态代理
基于类: cglib
java字节码实现: javassit
需要了解2个类:Proxy===代理, InvocationHandler:调用处理程序(接口)
InvocationHandler
Proxy
动态代理的好处:
可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
公共也就交给代理角色!实现业务的分工
公共业务发生扩展的时候,方便集中管理
一个动态代理类代理的是一个接口,一般就是对应的一类业务
一个动态代理类可以代理多个类,只要是实现了同一个接口即可
直接动态代理常用工具类(基于接口:JDK动态代理)
可以直接封装,使用
public class ProxyInvocationHandler implements InvocationHandler{
private Object target;//需要代理的接口(object可以是具体的接口名,target可以是任意名字)
public void SetTarget(Object target){
this.target=target;
}
//得到生成的代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass.getInterfaces,this);
}
//处理代理实例,返回结果(处理客户端选择执行的方法)
//Method:方法 Object[]:应该是参数 Proxy:代理类
public Object invoke(Object Proxy,Method method,Object[] args)throws Throwable{
//其他代理类额外添加的方法,写在这里面
Object result =method.invoke(target,args);//执行选择的方法
return result;
}
}
调用:
public Static void main(String[] args){
//创建真实角色(这一步没变)
UserServiceImpl userService=new UserServiceImpl();
//创建代理角色
ProxyInvocationHandLer pih =new ProxyInvocationHandLer();//调用之前的自动生成代理类方法
pih.setTarget(userService);//将需要代理的真实角色传递给他
UserService proxy=(UserService)pih.getProxy();//创建代理类(UserService:接口名)
proxy.query();//利用该代理类调用方法
}
11,AOP(动态代理)(核心还是反射)
提供声明式事务;允许用户自定义切面
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …(用AOP插入的情况)
切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
切入点(PointCut):执行方法的位置
连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aiSxyMag-1668096073686)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729161213739.png)]
即AOP在不改变原有代码的情况下,去增加新的功能
使用AOP置入,需要导入一个依赖包[重点]
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
<scope>runtime</scope>
</dependency>
实现AOP的第一种方式:(API接口)
1,编写业务接口和实现类
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add方法。。。");
}
@Override
public void delete() {
System.out.println("delete方法。。。");
}
@Override
public void update() {
System.out.println("update方法。。。");
}
@Override
public void select() {
System.out.println("select方法。。。");
}
}
2,编写增强类,后置和前置增强(Advisor)[继承不同的接口]
//MethodBeforeAdvice:前置增强
public class BeforeLog implements MethodBeforeAdvice {
//method: 要执行目标对象的方法
//args: 被调用方法的参数
//target: 目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法");
}
}
//AfterReturningAdvice:后置增强
public class AfterLog implements AfterReturningAdvice {
//returnValue 返回值
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法"+returnValue);
}
}
3,将这些类注册到Spring容器中,并实现aop切入,导入aop约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"//导入的
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop//导入的
https://www.springframework.org/schema/aop/spring-aop.xsd//导入的
">
<bean id="userService" class="com.kuang.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.kuang.log.BeforeLog"/>
<bean id="afterLog" class="com.kuang.log.AfterLog"/>
<!-- AOP配置 -->
<aop:config>
<!-- 配置切入点 expression:是一个表达式-->
<aop:pointcut id="pointcut" //这个名字可以随便取 expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>//固定写法
execution(* 哪一个类.*全部方法(..)//代表多个参数)
<!-- 执行环绕 advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
//aop:config 标签的完整意思:在UserServiceImpl这个实现类的所有方法中配置切入点,然后在执行这些方法时执行advice-ref定义的方法
</beans>
4.测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理的是接口,虽然绑定的对象是一个接口的实现类,但是返回值还是一个接口
UserService userService = (UserService) context.getBean("userService");
userService.delete();
}
}
实现AOP的第二种方式:(自定义类)
1,将之前的增强类变为一个普通类,里面写多个方法
public class MyAspect {
//方法就是之前的各种增强类,名字随便起
public void before(){
System.out.println("before通知=====");
}
public void after(){
System.out.println("after通知=======");
}
}
2,在spring中配置
<!-- 方式二: 自定义切面类 -->
<bean id="myAspect" class="com.kuang.def.MyAspect"/>
<aop:config>
<!--aspect:自定义切面 ref要引用的类 -->//自定义方法必须用aop:aspect这个标签
<aop:aspect ref="myAspect">
<!-- 切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<!-- 通知(要切入的方法) -->
<aop:before method="before" pointcut-ref="pointcut"/>
//我要切入ref这个类中的before()方法,在pointcut指定的切入点切入,什么时候切入?aop:before的时候
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
3,测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.delete();
}
}
实现AOP的第三种方式:(注解实现)
不需要导入注解的约束【因为不是用注解写对象】
只需要导入aop相关约束即可
在xml中需要新增一个标签
开启注解支持
<aop:aspectj-autoproxy/>
@Aspect //标注这个类是一个切面
public class AnnotationAspect {
//前置通知
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("执行方法前====");
}
//后置通知
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("执行方法后=====");
}
//环绕通知
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前=====");
jp.proceed(); //执行方法
System.out.println("环绕后=====");
}
}
12,整合mybatis
mybatis是写接口,写mapper.xml文件,写SQL
Spring就是万物皆对象,每一个类都是一个对象,在beans.xml中实例化之后才可以使用
步骤:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y0VBwCly-1668096073686)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729180248262.png)]
-
导入相关jar包
-
junit
-
mybatis
-
mysql
-
spring
-
AOP
-
spring-jdbc【新的】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8PNVdPOm-1668096073687)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729180039423.png)]
-
mybatis-spring【新的】
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4u8MewxF-1668096073687)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729180200901.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyK3Ymev-1668096073687)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220729175925321.png)]
-
1,编写配置文件
2,测试
12.1 回顾mybatis
- 编写实体类
- 编写核心配置文件
- 编写接口
- 编写Mapper.xml
- 测试
12.2 整合mybatis方式一
利用SqlSessionTemplate。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJKEh0Il-1668096073688)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730135529798.png)]
第一步:编写数据源
在spring的配置文件中编写以下代码
<!-- DataSource:使用Spring的数据源替换mybatis的配置 c3p0 dbcp druid
这里使用spring提供的jdbc: 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/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
第二步:创建SqlSessionFatory对象
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis配置文件(这两句可不写)-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kuang/mapper/*.xml"/>//意思是:相当于绑定注册,绑定com/kuang/mapper/*.xml这个路径下的所有Mapper.xml
</bean>
第三步:创建sqlsession对象(在spring中叫sqlSessionTemplate)
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能用有参构造器注入sqlSessionFactory 因为它没有set方法 -->
<constructor-arg ref="sqlSessionFactory"/>
</bean>
//第二,第三步相当于之前写的mybatis工具类
如何使用?
1,需要给接口加实现类
在原来mybatis的基础上给mapper接口添加实现类(实现类去实现具体的调用步骤)
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSession;//在实现类中定义一个SqlSessionTemplate对象
public void setSqlSession(SqlSessionTemplate sqlSession) {//需要写一个set方法,一会方便在beans.xml中进行注入
this.sqlSession = sqlSession;
}
@Override//这就是类似指定调用的SQL语句
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);//还是需要映射一个接口的对象调用方法执行SQL
List<User> userList = mapper.selectUser();//执行SQL
return userList;
}
}
2,将自己写的实现类,注入到spring中(创建一个实现类的对象)
<!-- 总的spring配置文件 -->
<bean id="UserMapperImpl" class="com.kuang.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>//将SQLsession对象注入
</bean>
3,测试
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
遇见的错误
mybatis-config.xml里的mappers 要删了,
这个和 spring-dao的
<property name="mapperLocations"value="classpath:com/lzh/mapper/*.xml"/>
只能二选一!!!!!!不然会报错的,都是mapper的作用!
12.3 整合mybatis方式二
利用继承SqlSessionDaoSupport
两个点:不用再配置sqlsession对象,实现类继承
在实现类中写:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8cxGvJ2s-1668096073688)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730162524102.png)]
在beans.xml中写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nTgsIUz-1668096073688)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730162708466.png)]
相当于省略了beans.xml中配置sqlsession的步骤
13,事务
spring中的事务管理分为
- 声明式事务:AOP(不影响原有代码)
- 编程式事务:需要在代码中进行配置管理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XjhFgLkD-1668096073689)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220730170603636.png)]
步骤:
1,配置声明式事务(开启事务管理器)
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--这是死代码 ,dataSource:数据源-->
2,结合AOP实现事务的织入
<!--1, 导入事务的约束 (头文件处)-->
//注意aop的约束也要导入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"//导入的
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx//导入的
https://www.springframework.org/schema/tx/spring-tx.xsd //导入的
">
<!--2, 配置事务通知(方法) -->
<!-- transactionManager步骤一所配置的bean -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 给哪些方法配置事务 -->
<!-- 配置事务的传播特性: propagation=“required” spring默认事务 -->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>//所有add方法配置事务
意思是:给所有的add方法配置REQUIRED类型的事务
<tx:method name="delete" propagation="REQUIRED"/>//所有delete方法配置事务
<tx:method name="query" read-only="true"/>//read-only表示query类的事务无法对数据库进行增删改
<tx:method name="*"/>//所有方法配置事务
</tx:attributes>
</tx:advice>
<!--3,配置事务切入(切入点) -->
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* com.kuang.mapper.*.*(..))"/>切入点
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>//切入的方法
</aop:config>
AOP:切面,通知,切入点
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况下
- 如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务
- 事务在项目开发中重要,设计到数据的一致性和完整性问题,不容马虎