Spring 笔记
1.认识Spring
1.1简介
- Spring是一个开源免费的框架 , 容器
- Spring是一个轻量级的框架 , 非侵入式的
- 控制反转 IoC , 面向切面 Aop
- 对事物的支持 , 对框架的支持
一句话概括:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
1.2组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
- Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
1.3拓展
在Spring的官网有这个介绍:现代化的Java开发!说白了就是基于Spring的开发
- Spring Boot与Spring Cloud
Spring Cloud是基于Spring Boot实现的;
Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;
Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 ,
Spring Cloud很大的一部分是基于Spring Boot来实现,
Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。
SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot。
2.IoC(Inversion of Control)
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。
没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,
控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
使用IoC的目的:解耦
2.1IoC创建对象的方式
2.1.1 搭建环境
User.java
public class User { private String name; public void show(){ System.out.println("name:"+name); } public String getName() { return name; } public void setName(String name) { this.name = name; } public User(String name) { this.name = name; } public User() { } }
beans.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"> </beans>
2.1.2通过无参构造器创建对象
<!-- id:获取该类对象的名字 class:该类的路径 property:设置变量名的值 name:变量名 value:该变量名的值 --> <bean id="user" class="com.spring.pojo.User"> <property name="name" value="无参构造器创建"></property> </bean>
注意:如果类中存在有参构造器,那么也要显式的声明无参构造器,因为使用上面的方法创建对象调用的是无参构造器。
2.1.3通过有参构造器创建对象
有三种方式
<!-- 有参构造器注入的三种方式-->
<!-- 第一种:下标-->
<bean id="user1" class="com.gsy.pojo.User">
<constructor-arg index="0" value="gsy1"></constructor-arg>
</bean>
<!-- 第二种:通过类型-->
<bean id="user2" class="com.gsy.pojo.User">
<constructor-arg type="java.lang.String" value="gsy2"></constructor-arg>
</bean>
<!-- 第三种,通过参数名-->
<!-- type指参数类型,一般情况下参数类型都不一样,如果一样,按顺序来 -->
<bean id="user3" class="com.gsy.pojo.User">
<constructor-arg name="name" value="gsy3"></constructor-arg>
</bean>
当配置文件被加载时,bean全都被实例化,即使你没有用getBean()方法去取它,这是由于:
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
3 Spring配置
3.1别名
<alias name="user1" alias="u1"/>
3.2Beans.xml的配置
<!--bean就是java对象,由Spring创建和管理--> <!-- id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符 如果配置id,又配置了name,那么name是别名 name可以设置多个别名,可以用逗号,分号,空格隔开 如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象; class是bean的全限定名=包名+类名 --> <bean id="user" name="hello2 h2,h3;h4" class="com.kuang.pojo.User"> <property name="name" value="Spring"/> </bean>
3.3improt
创建了多个beans.xml可以都引入到一个里面,其他被引用的配置文件中的bean也会自动装配。
<import resource="{path}/beans.xml"/> <import resource="beans.xml"/> <import resource="beans2.xml"/> <import resource="beans3.xml"/>
4 DI依赖注入
- 依赖注入(Dependency Injection,DI)。
- 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
- 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .
4.1构造器依赖注入
见1.1.2/1.1.3
4.2 set注入(重点)
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .
搭建环境:
Address.java
package com.gsy.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Address{" + "address='" + address + '\'' + '}'; } }
Student.java
public class Student { private String name; private Address address; private String[] books; private List<String> hobby; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobby() { return hobby; } public void setHobby(List<String> hobby) { this.hobby = hobby; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address.toString() + ", books=" + Arrays.toString(books) + ", hobby=" + hobby + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }
beans.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"> <bean id="address" class="com.gsy.pojo.Address"> <property name="address" value="河南"></property> </bean> <bean id="student" class="com.gsy.pojo.Student"> </bean> </beans>
set注入的方式
- 普通值注入,value
<!-- 第一种,普通值注入,value--> <property name="name" value="鸡哥"></property>
2. Bean注入,ref
<!-- 第二种,Bean注入,ref--> <property name="address" ref="address"></property>
3.数组
<!-- 数组--> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>三国演义</value> <value>水浒传</value> </array> </property>
4. list
<!-- list--> <property name="hobby" > <list> <value>唱</value> <value>跳</value> <value>rap</value> <value>篮球</value> </list> </property>
5.map
<!-- map--> <property name="card"> <map> <entry key="身份证" value="123456"/> <entry key="银行卡" value="654321"/> </map> </property>
6. Set
<!-- Set--> <property name="games"> <set> <value>cf</value> <value>lol</value> <value>cs</value> </set> </property>
7.null
<!-- null--> <property name="wife"> <null/> </property>
8.properties
<!-- properties--> <property name="info"> <props> <prop key="鸡哥">个人练习生</prop> <prop key="时长">两年半</prop> <prop key="爱好">你干嘛哎哟</prop> </props> </property>
这里的key是键,标签中间的是值,相当于value
4.3拓展注入实现
User.java
public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
1.P命名空间注入 :
需要在头文件中加入约束文件
xmlns:p="http://www.springframework.org/schema/p"
<!-- p:(properties)命名空间,属性依然要设置set方法--> <bean id="student2" class="com.gsy.pojo.Student" p:name="ikun"> </bean>
2.C命名空间注入 :
需要在头文件中加入约束文件
xmlns:c="http://www.springframework.org/schema/c"
<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="user" class="com.gsy.pojo.User" c:name="ikunkun" c:age="25"> </bean>
用c命名空间需要有参构造器,参数要写完整。
4.4 Bean作用域
在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。
简单地讲,bean就是由IoC容器初始化、装配及管理的对象 。
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。
4.4.1 singleton 单例模式
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。
注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<!-- singleton单例模式 spring默认单例模式--> <bean id="student2" class="com.gsy.pojo.Student" p:name="ikun" scope="singleton"> </bean>
4.4.2 prototype 原型模式
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。
Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
在XML中将bean定义成prototype,可以这样配置:
<!-- prototype原型模式:每次从容器中get时都会产生一个新的对象--> <bean id="user" class="com.gsy.pojo.User" c:name="ikunkun" c:age="25" scope="prototype"> </bean>
5.自动装配
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean。
Spring中bean有三种装配机制,分别是:
- 在xml中显式配置;
- 在java中显式配置;
- 隐式的bean发现机制和自动装配。
这里我们主要讲第三种:自动化的装配bean。
Spring的自动装配需要从两个角度来实现,或者是两个操作:
组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显式的配置降低到最少。
5.1环境搭建
cat.java
public class Cat { public void shout(){ System.out.println("miao~"); } }
dog.java
public class Dog { public void shout(){ System.out.println("wang~"); } }
person.java
public class Person { private Cat cat; private Dog dog; private String name; public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
beans.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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.gsy.pojo.Cat"/> <bean id="dog" class="com.gsy.pojo.Dog"/> <bean id="person" class="com.gsy.pojo.Person" > <property name="name" value="gsy"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean> </beans>
5.2 byName
autowire byName (按名称自动装配)
由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。
采用自动装配将避免这些错误,并且使配置简单化。
1.修改bean配置,增加一个属性 autowire=”byName”
<bean id="cat" class="com.gsy.pojo.Cat"/> <bean id="dog" class="com.gsy.pojo.Dog"/> <bean id="person" class="com.gsy.pojo.Person" autowired="byName"> <property name="name" value="gsy"/> </bean>
2.经测试正常运行。
3.如果将bean 的id改为cat1,再次测试, 执行时报空指针java.lang.NullPointerException。
4.这是因为按byName规则找不到对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。
小结:
当一个bean节点带有 autowire byName的属性时。
- 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常。
5.3 byType
autowire byType (按类型自动装配)
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报NoUniqueBeanDefinitionException(不唯一)的异常。
测试:
1.将byName改为byType
2.再注册一个cat的bean id为cat2
3.测试报错NoUniqueBeanDefinitionException
4.删除cat2 正常运行
因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
5.4 使用注解
jdk 1.5支持注解 spring2.5支持注解
1.配置beans.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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> .... </beans>
2.开启属性注解支持
<!--开启属性注解支持--> <context:annotation-config/>
5.4.1 @Autowired
@Autowired是首先根据byType配置的。
注意,我说的是首先根据byType配置的,为什么是首先呢?因为@Autowired如果发现有冲突,其实会自动转去byName去匹配
只有都两个匹配不到的时候才会报错
Person.java
public class Person { @Autowiredprivate Cat cat; @Autowiredprivate Dog dog; private String name; public Cat getCat() { return cat; } public Dog getDog() { return dog; } public String getName() { return name; } }
此时可以删掉set方法,因为注解是通过反射实现的。
@Autowired(required=false) 说明: false,对象可以为null;true,对象必须存对象,不能为null。(默认为true)
@Autowired配合@Qualifier(value="xxx")指定一个唯一的bean对象注入。
@Autowired @Qualifier(value = "dog2") private Dog dog;
5.4.2 @Resource
- 如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
@Resource
可以指定一个name
属性。虽然这个是根据byName
来的,但是却和通过xml配置的byName不一样。byName
是by类中的setter方法来的。而基于注解的则是根据属性的名字来的,和setter方法没有任何关系。如果name属性不配置上@Resource(name="xxx"),默认就是属性名对应的实现类。
5.4.3 小结
@Autowired与@Resource异同:
@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
@Autowired默认先按类型装配(属于spring规范),找不到时按名字装配。默认情况下必须要求依赖对象必须存在。
如果要允许null 值,可以设置它的required属性为false,
如:@Autowired(required=false) ,如果我们想指定名称装配可以结合@Qualifier注解进行使用
@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,
如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
6.使用注解开发
6.1说明
Spring4之后若要使用注解要引入AOP的包
6.2 Bean
1.在xml中配置扫描指定的包
<!--扫描指定的包,该包下的注解生效--> <context:component-scan base-package="com.gsy.pojo"/>
2.在该包下编写类 User.java 添加注解
import org.springframework.stereotype.Component; //@Component 组件 //该注解等价于在xml中注册了一个bean //相当于配置文件中 <bean id="user" class="当前注解的类"/> @Component("user") public class User { public String name="gsy"; }
6.3 属性注入
1.直接在属性上加注解 @value("xxx")
2.也可以在set方法上使用该注解
6.4 衍生注解
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
@Controller:web层
@Service:service层
@Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配了!
6.5 自动装配注解
@Autowired:自动装配 先byType再byName 配合@Qualifier(value="xxx")可以指定装配bean id为value的
@Nullable: 字段标记这个注解代表这个字段可以为null
@Resource: 自动装配,先byName再byType
@Component:组件,放在类上,代表该类被Spring管理了,也就是bean。
6.6 作用域
@scope("xxx")
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
- prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
6.7 小结
XML与注解比较
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,维护相对复杂
xml与注解整合开发 :推荐最佳实践
- xml管理Bean
- 注解完成属性注入
- 使用过程中, 要使用注解就要开启注解的支持
<!--开启属性注解支持--> <context:annotation-config/>
6.8 基于Java类进行配置
JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,
在 Spring4 的版本, JavaConfig 已正式成为 Spring4 的核心功能 。
1.User.java
@Component public class User { @Value("gsy") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
2.Myconfig.java 配置类
//会被Spring容器托管,注册到容器中 它本来也是一个@Component //@Configuration 代表这是一个配置类 @Configuration @ComponentScan("com.gsy.pojo")//可以显式配置也可以不写 @Import(Myconfig2.class)//引入其他配置类 public class Myconfig { //注册一个Bean 相当于在xml中的<bean/> @Bean public User getUser(){//此方法的id就相当于bean标签中的id属性 return new User();//返回值相当于bean标签中的class属性 } }
3.测试
public class Mytest { public static void main(String[] args) { //完全使用配置类的方式就只能用过AnnotationConfigApplicationContext来获取上下文,通过配置类的Class对象加载 ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class); User user = context.getBean("getUser", User.class); System.out.println(user.getName()); } }
7.代理模式
7.1静态代理
静态代理角色分析
- 抽象角色 : 一般使用接口或者抽象类来实现
- 真实角色 : 被代理的角色
- 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
- 客户 : 使用代理角色来进行一些操作 .
1.Rent.java 抽象角色
//租房 public interface Rent { public void rent(); }
2.Host.java 真实角色
public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子"); } }
3.Proxy.java 代理

public class Proxy implements Rent{ private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } public void rent(){ seeHouse(); host.rent(); hetong(); fare(); } //看房 public void seeHouse(){ System.out.println("中介带你看房"); } //合同 public void hetong(){ System.out.println("签合同"); } //收中介费 public void fare(){ System.out.println("收中介费"); } }
4.Client.java 客户
public class Client { public static void main(String[] args) { //房东要租房子 Host host=new Host(); //代理,中介帮房东租房子,但是有一些附属操作 Proxy proxy = new Proxy(host); //你不用面对房东,直接找中介 proxy.rent(); } }
9.2、静态代理的好处
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
公共的业务由代理来完成 . 实现了业务的分工 ,
公共业务发生扩展时变得更加集中和方便 .
缺点 :
类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !
7.2动态代理
动态代理的角色和静态代理的一样 .
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
基于接口的动态代理——JDK动态代理
基于类的动态代理—cglib
现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
我们这里使用JDK的原生代码来实现,其余的道理都是一样的!
JDK的动态代理需要了解两个类
核心 : InvocationHandler 和 Proxy
1.Rent.java
2.Host.java
3.ProxyInvocationHandler.java
public class ProxyInvocationHandler implements InvocationHandler { private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色 public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this); } // proxy : 代理类 // method: 代理类的调用处理程序的方法对象. // 处理代理实例上的方法调用并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //核心利用反射实现 Object result = method.invoke(rent, args); fare(); return result; } public void seeHouse() { System.out.println("看房"); } public void fare() { System.out.println("收中介费"); } }
4.Client.java
public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理实例的调用处理程序 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setRent(host);//将真实角色放进去 Rent proxy = (Rent) pih.getProxy();//动态生成对应的代理类 proxy.rent(); } }
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
7.2.1动态代理的好处
静态代理有的它都有,静态代理没有的,它也有!可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
公共的业务由代理来完成 . 实现了业务的分工 ,
公共业务发生扩展时变得更加集中和方便 .
8.AOP面向切面编程
使用Spring实现AOP 要导入包:
pom.xml
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.19</version> </dependency>
搭建环境:
UserService.java
public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
UserServiceImpl.java
public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加用户"); } @Override public void delete() { System.out.println("增删除用户"); } @Override public void update() { System.out.println("修改用户"); } @Override public void select() { System.out.println("查询用户"); } }
Log.java
public class Log 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()+"被执行了"); } }
AfterLog.java
public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue); } }
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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 注册bean--> <bean id="userService" class="com.gsy.service.UserServiceImpl"/> <bean id="log" class="com.gsy.log.Log"/> <bean id="afterlog" class="com.gsy.log.AfterLog"/> </beans>
8.1方式一:使用原生的Spring API接口
<!-- 第一种方式:使用原生的Spring API接口--> <!-- 配置aop:需要导入aop的约束--> <aop:config> <!--切入点:expression:表达式,excution(要执行的位置!****)--> <aop:pointcut id="pointcut1" expression="execution(* com.gsy.service.UserServiceImpl.*(..))"/> <!-- 执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut1"/> <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut1"/> </aop:config>
8.2方式二:自定义类
<!--方式二:自定义类--> <bean id="diy" class="com.gsy.diy.DiyPointCut"/> <aop:config> <!--自定义切面,ref要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.gsy.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
8.3方式三:使用注解
AnnotationPointCut.java
@Aspect//标注这个类是一个切面 public class AnnotationPointCut { @Before("execution(* com.gsy.service.UserServiceImpl.*(..))") public void before(){ System.out.println("方法执行前"); } @After("execution(* com.gsy.service.UserServiceImpl.*(..))") public void after(){ System.out.println("方法执行后"); } // 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点 @Around("execution(* com.gsy.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); Signature signature = jp.getSignature();//获得签名 System.out.println("signature"+signature); Object proceed = jp.proceed();//执行方法 System.out.println("环绕后"); System.out.println(proceed); } }
xml配置
<!-- 方式三 注解--> <bean id="annotationPointCut" class="com.gsy.diy.AnnotationPointCut"/> <!-- 开启注解支持--> <aop:aspectj-autoproxy/>
运行结果顺序:环绕前->方法执行前->执行方法->环绕后->方法执行后
9.整合Mybatis
导包
pom.xml
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.32</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.26</version> <scope>provided</scope> </dependency> <!-- Spring操作数据库还需要spring-jdbc--> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.0.8</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version> 1.9.19</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version> 3.0.1</version> </dependency>
spring6和springBoot3之后只支持JDK17!
User.java (和8中一样)
UserMapper.java
public interface UserMapper { public List<User> selectUser(); }
UserMapperImpl.java
public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; public void setSqlSession(SqlSessionTemplate sqlSession){ this.sqlSession=sqlSession; } @Override public List<User> selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } }
mybatis-config.xml 核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置文件--> <configuration> <typeAliases> <package name="com.gsy.pojo.User"/> </typeAliases> </configuration>
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.gsy.mapper.UserMapper"> <select id="selectUser" resultType="com.gsy.pojo.User"> select * from mybatis.user </select> </mapper>
spring-dao.xml
- 数据源
- sqlSessionFactory
- sqlSessionTamplate
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- Datasource:使用spring的数据源替换mybatis的配置--> <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="gsy666"/> </bean> <!-- sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"/> <!-- 绑定Maybtis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/gsy/mapper/*.xml"/> </bean> <!-- sqlSessionTemplate 即SqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!-- 只能使用构造器注入,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> </beans>
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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--导入spring-dao.xml--> <import resource="spring-dao.xml"/> <bean id="userMapper" class="com.gsy.mapper.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
方式二:
UserMapperImpl2.java
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser(){ return getSqlSession().getMapper(UserMapper.class).selectUser(); } }
applicationContext.xml
<bean id="userMapper2" class="com.gsy.mapper.UserMapperImpl2"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
10.spring声明事务
使用事务要先添加tx约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx">
xml
<!-- 事务管理器--> <!-- JDBC事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource"/> </bean> <!-- 结合AOP实现事务的注入--> <!-- 配合事务通知:--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.gsy.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律