Spring
简介:
Spring :春天
Spring是2002,它由Rod Johnson创建
2004年,Spring发布了1.0版本
jar包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
优点:
Spring 是一个开源免费的框架 、 轻量级的框架 、非入侵式的、控制反转IOC、面向切面AOP、对事务的支持、对框架的支持
Spring是一个轻量级的控制反转(IoC)(Inversion of Control)和面向切面(AOP)(Aspect Oriented Programming)的容器框架。
Spring
核心容器
这是Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。BeanFactory是工厂模式的一个实现,它使用IoC将应用配置和依赖说明从实际的应用代码中分离出来。
应用上下文(Context)模块
核心模块的BeanFactory使Spring成为一个容器,而上下文模块使它成为一个框架。
这个模块扩展了BeanFactory的概念,增加了对国际化(I18N)消息、事件传播以及验证的支持。
另外,这个模块提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如Velocity和FreeMarker集成的支持。
Spring的AOP模块
Spring在它的AOP模块中提供了对面向切面编程的丰富支持。
这个模块是在Spring应用中实现切面编程的基础。
为了确保Spring与其它AOP框架的互用性,Spring的AOP支持基于AOP联盟定义的API。AOP联盟是一个开源项目,它的目标是通过定义一组共同的接口和组件来促进AOP的使用以及不同的AOP实现之间的互用性。通过访问他们的站点,你可以找到关于AOP联盟的更多内容。
Spring的AOP模块也将元数据编程引入了Spring。使用Spring的元数据支持,你可以为你的源代码增加注释,指示Spring在何处以及如何应用切面函数。
JDBC抽象和DAO模块
使用JDBC经常导致大量的重复代码,取得连接、创建语句、处理结果集,然后关闭连接。
Spring的JDBC和DAO模块抽取了这些重复代码,因此你可以保持你的数据库访问代码干净简洁,并且可以防止因关闭数据库资源失败而引起的问题。
这个模块还在几种数据库服务器给出的错误消息之上建立了一个有意义的异常层。使你不用再试图破译神秘的私有的SQL错误消息!
另外,这个模块还使用了Spring的AOP模块为Spring应用中的对象提供了事务管理服务。
对象/关系映射集成模块
对那些更喜欢使用对象/关系映射工具而不是直接使用JDBC的人,Spring提供了ORM模块。Spring并不试图实现它自己的ORM解决方案,而是为几种流行的ORM框架提供了集成方案,包括Hibernate、JDO和iBATIS SQL映射。Spring的事务管理支持这些ORM框架中的每一个也包括JDBC。
Spring的Web模块
Web上下文模块建立于应用上下文模块之上,提供了一个适合于Web应用的上下文。另外,这个模块还提供了一些面向服务支持。例如:实现文件上传的multipart请求,它也提供了Spring和其它Web框架的集成,比如Struts、WebWork。
Spring的MVC框架
Spring为构建Web应用提供了一个功能全面的MVC框架。虽然Spring可以很容易地与其它MVC框架集成,例如Struts,但Spring的MVC框架使用IoC对控制逻辑和业务对象提供了完全的分离。
它也允许你声明性地将请求参数绑定到你的业务对象中,此外,Spring的MVC框架还可以利用Spring的任何其它服务,例如国际化信息与验证。
拓展
Springboot和SpringCloud
Springboot是Spring的一套快速的配置脚手架,可以基于Springboot快速的开发单个的微服务,SpringCloud是基于SpringBoot实现的。
IOC
IOC的本质
控制反转(inversion of control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法
在没有IOC的程序中,我们使用面向对象编程,对象的创建和对象间依赖关系完全在程序中,对象的创建是由程序自己控制。
控制反转后,将对象创建的事情转移给第三方。
控制反转其实就是获得依赖对象的方式反转了
IOC是Spring框架的核心内容,可以使用多种方式实现IOC,比如,xml配置、注解....
spring文档
Spring Framework 中文文档 - Spring Framework 4.3.21.RELEASE Reference | Docs4dev
HelloSpring
1,新建模块、导入jar包
添加一个commons-logging的依赖
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
新建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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
新建实体类Hello
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("hello"+name);
}
}
配置beans.xml
<!--
bean就是java对象,由spring创建和管理
id就是变量名 class= new的对象
property相当于给属性设置的一个值,利用set方法
-->
<bean id="hello" class="com.iweb.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
测试
@Test
public void test(){
//解析beans.xml文件,生成管理相应的Bean对象
//生成好的对象都在spring中管理,要使用,直接去里面取出来就好了
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getbean:参数即为bean中的id
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
问题思考:
1,hello对象是由谁创建?
由spring创建
2,hello对象的属性是谁设置的?
由spring容器设置的
这个过程就叫控制反转
控制:谁来控制对象的创建,传统的应用程序的对象就程序本身控制创建的,使用spring以后,对象由Spring来创建。
反转:程序本身不创建对象,而是变成了被动的接收对象
依赖注入:利用set方法进行注入的。
IOC是一种编程思想,由主动的编程变为被动的接收
到了这个时候,我们再也不用去程序中改动代码了,要实现不同的操作,只需要在xml配置用进行修改。
IOC可以总结为:对象由spring来创建、管理、装配。
自制简易版IOC
public interface UserDao {
String getUser();
}
public class UserDaoImpl implements UserDao{
@Override
public String getUser() {
return "user1";
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="com.iweb.dao.UserDaoImpl" ></bean>
</beans>
public class ClassPathApplicationContext {
private static Map<String,Object> beanMap = new HashMap<>();
private String filePath;
public ClassPathApplicationContext(String filePath) {
this.filePath = filePath;
load();
}
//执行加载对象的方法
private void load(){
try {
//根据传进来的文件路径,加载文件
String path = ClassPathApplicationContext.class.getClassLoader().
getResource(filePath).getPath();
//给path指定编码格式
path = URLDecoder.decode(path, "utf-8");
//通过JSOUP去解析
Document document = Jsoup.parse(new File(path), "utf-8");
Elements beans = document.getElementsByTag("bean");
//遍历bean标签
if (beans != null && beans.size() > 0){
for (int i = 0; i < beans.size(); i++) {
Element bean = beans.get(i);
//获取bean中的属性值 id 、 class
String calssName = bean.attr("class");
String id = bean.attr("id");
//通过反射创建对象
Class<?> clazz = Class.forName(calssName);
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
//将对象放入到map
beanMap.put(id,obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Object getBean(String id){
return beanMap.get(id);
}
}
public class Demo {
public static void main(String[] args) {
ClassPathApplicationContext context =
new ClassPathApplicationContext("beans.xml");
UserDao userDao = (UserDao) context.getBean("userDao");
System.out.println(userDao);
System.out.println(userDao.getUser());
}
}
IOC创建对象的方式
1,通过无参构造方法来创建
实体类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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.iweb.pojo.User">
<property name="name" value="zhangsan"/>
</bean>
</beans>
测试使用
@Test
public void test(){
Object object;
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();
}
通过结果发现,在调用show方法之前,User对象已经通过无参的构造初始化了
2,通过有参构造方法来创建
实体类UserT,写一个有参构造方法
public class UserT {
private String name;
public UserT(String name) {
this.name = name;
System.out.println("UserT被创建了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
beans.xml 三种配置方法
<!-- 第一种:根据index参数下标设置-->
<!-- <bean id="userT" class="com.iweb.pojo.UserT">-->
<!-- <constructor-arg index="0" value="张三2"/>-->
<!-- </bean>-->
<!-- 第二种:根据参数名字设置-->
<!-- <bean id="userT" class="com.iweb.pojo.UserT">-->
<!-- <constructor-arg name = "name" value="张三3"/>-->
<!-- </bean>-->
<!-- 第三种:根据参数的类型来设置
不推荐,多个参数类型相同不好设置-->
<bean id="userT" class="com.iweb.pojo.UserT">
<constructor-arg type="java.lang.String" value="张三4"/>
</bean>
测试:
结果:
总结:在配置文件加载的时候,其中管理的对象都已经初始化了
Spring配置
1,别名的使用
<alias name="userT" alias="user123" />
配置以后,可以直接在context中getBean后面传入 “ user123 ”就可以了
2,Bean的配置
<!-- id就是bean的标识符,要唯一,如果没有配置的话,name值就是默认标识符,
如果配置了id,又配置了name,那么name就是别名
name可以设置多个别名,可以用逗号、空格、分号隔开
如果不配置id和name,通过context.getBean("com.iweb.pojo.User")获取对象
class 是bean的全类名 = 包名+类名
-->
<!-- id就是bean的标识符,要唯一,如果没有配置的话,name值就是默认表示符,
如果配置了id,又配置了name,那么name就是别名
name可以设置多个别名,可以用逗号、空格、分号隔开
如果不配置id和name,通过context.getBean("com.iweb.pojo.User")获取对象
class 是bean的全类名 = 包名+类名
-->
<bean id="user" name="user1 user2,user3;user4" class="com.iweb.pojo.User">
<property name="name" value="zhangsan"/>
</bean>
3,import
团队合作通过import实现
<import resource="./beans1.xml"/>
依赖注入(DI)
依赖:指的是Bean对象的创建依赖于容器
注入:指bean对象所依赖的资源,由容器来设置和装配
就是指: 控制翻转的对象的创建,已经对象属性的赋值,都是由spring容器来完成
1,构造器注入(之前使用的就是构造方法注入)
2,set注入(重点)
要求被注入的属性,必须有set方法,set方法的方法名由 set+属性的首字母大写。
如果属性是boolean类型,没有set方法,是is
新建一个模块项目
导入jar包
新建一个实体类
Student
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private String study;
private Properties info;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public void setGames(Set<String> games) {
this.games = games;
}
public void setStudy(String study) {
this.study = study;
}
public void setInfo(Properties info) {
this.info = info;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public String[] getBooks() {
return books;
}
public List<String> getHobbys() {
return hobbys;
}
public Map<String, String> getCard() {
return card;
}
public Set<String> getGames() {
return games;
}
public String getStudy() {
return study;
}
public Properties getInfo() {
return info;
}
public void show(){
System.out.println("name="+name+",address="+address.toString()+",books="+books);
for (String book : books) {
System.out.println("<<"+book+">>,");
}
System.out.println("\n爱好:"+hobbys);
System.out.println("card:"+card);
System.out.println("games:"+games);
System.out.println("study:"+study);
System.out.println("info:"+info);
}
}
类Address
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 + '\'' +
'}';
}
}
新建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="student" class="com.iweb.pojo.Student">-->
<!-- <property name="name" value="小明"/>-->
<!-- </bean>-->
<!-- bean注入,需要一个对象的引用-->
<bean id="addr" class="com.iweb.pojo.Address">
<property name="address" value="南京"/>
</bean>
<bean id="student" class="com.iweb.pojo.Student">
<property name="name" value="小明"/>
<property name="address" ref="addr"/>
<!-- 数组的注入-->
<property name="books">
<array>
<value>java学习</value>
<value>数据库学习</value>
<value>框架学习</value>
</array>
</property>
<!-- list注入-->
<property name="hobbys">
<list>
<value>打球</value>
<value>玩游戏</value>
<value>看电影</value>
</list>
</property>
<!-- map注入-->
<property name="card">
<map>
<entry key="中国银行" value="12346941661616"/>
<entry key="工商银行" value="8465155615153153"/>
</map>
</property>
<!-- set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>王者荣耀</value>
<value>下棋</value>
</set>
</property>
<!-- null注入-->
<property name="study"><null/></property>
<!-- properties注入-->
<property name="info">
<props>
<prop key="学号">20210011</prop>
<prop key="性别">男</prop>
<prop key="姓名">小明</prop>
<prop key="driver">com.mysql.jdbc.Driver</prop>
<prop key="name">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
测试
public class MyTest {
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = context.getBean("student", Student.class);
student.show();
}
}
Bean的作用域
在spring中,那些组成应用程序的主体以及由Sping IOC容器所管理的对象,都称之为bean。
bean就是由ioc容器初始化、装配及管理的对象
1,singleton单例模式
当一个bean的作用域为singleton,那么ioc容器中只会存在一个共享的bean示例,并且所有对bean的请求,只要id和该bean定义相匹配,则返回bean的同一个实例。
<bean id="user" class="com.iweb.pojo.User" c:name="张三" c:age="20" scope="singleton"/>
singleton的作用域是spring的默认作用域,要在xml中将bean定义成singleton,可以在在bean中加入scope="singleton"
测试,通过测试发现,获取的同一个实例都是相等的
2,prototype模式 原型模式
当一个bean的作用域为prototype,表示一个bean定义对应多个对象实例,prototype作用有的bean会导致每次对该bean请求时,都会创建一个新的bean实例。
prtotype是原型类型,它在我们创建容器的时候并没有实例化,而是在我们获取bean的时候才会去创建一个对象,而且每次我们获取到的对象都不是同一个对象。
一般的话,对有状态的bean应该使用prototype作用域,对无状态的bean使用singleton作用域
有状态会话bean :每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初都会得到一个初始的bean。
无状态会话bean :bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。
scope=singleton(默认,单例,生成一个实例) 不是线程安全,性能高
scope=prototype(多线程, 生成多个实例)
使用方式:
<bean id="user" class="com.iweb.pojo.User" c:name="张三" c:age="20" scope="prototype"/>
测试
request,session,application这几个作用域都是基于web
3,request
将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期;也就是说,每个 HTTP 请求都有一个自己的 bean 实例,它是在单个 bean 定义的后面创建的。仅在可感知网络的 Spring ApplicationContext中有效。
XML配置
<bean id="loginController" class="com.example.LoginController" scope="request"/>
注解方式
//@Scope("request")
@RequestScope
@RestController
public class LoginController{
private int flag = 0;
@RequestMapping("/login")
public int login(){
return flag++;
}
}
启动项目,访问得到的效果与Prototype的一样,每刷新一次页面就会有一个新的请求,创建的是一个新的实例;与Prototype不同的是request不需要开发人员去实现bean后处理器来清除实例,这些生命周期由web容器接管。
4,session
将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
<bean id="userController" class="controller.UserController" scope="session"/>
注解方式
@RestController
//@Scope("session")
@SessionScope
public class UserController {
private int flag = 0;
@RequestMapping("/getUser")
public int getUser(){
return flag++;
}
}
5,application
Spring容器通过对整个web应用使用一次bean定义来创建一个bean的新实例。也就是说,该bean的作用域在ServletContext级别,并存储为常规的ServletContext属性。
这有点类似于Spring单例bean,但在两个重要方面不同:
它是每个ServletContext的一个单例,而不是每个Spring ‘ApplicationContext’(在任何给定的web应用程序中可能有几个);
它实际上是暴露的,因此可以作为可见的ServletContext属性。
<bean id="userController" class="controller.UserController" scope="application"/>
注解使用方式
@RestController
//@Scope("application")
@ApplicationScope
public class ServletController {
private int flag = 0;
@RequestMapping("/get")
public int login(){
return flag++;
}
}
Bean的自动装配
自动装配是使用spring满足bean依赖的一种方法
spring会在应用上下文中自动为某个bean寻找其依赖的<bean>
spring中bean有三种装配机制
1,在xml显式的配置
2,在java显式 配置
3,隐式的bean发现机制和自动装配
自动装配Bean,需要从两个角度来实现:
1,组件扫描:spring会自动的发现应用上下文中所创建的bean
2,自动装配:spring自动满足bean之间的依赖,也就是IOC/DI
使用
创建新模块
新建实体类
cat
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
dog
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
User
public class User {
private Cat cat;
private Dog dog;
private String str;
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 getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
beans配置
<?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.iweb.pojo.Cat"/>
<bean id="dog" class="com.iweb.pojo.Dog"/>
<bean id="user" class="com.iweb.pojo.User">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="str" value="zhangsan"/>
</bean>
</beans>
测试
public class MyTest {
@Test
public void testMethodAutowire(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user", User.class);
user.getCat().shout();
user.getDog().shout();
}
}
byName
按照名称自动装配
在手动配置xml过程中,常常发生字母缺漏、大小写错误,而且不容易发现。
使用自动装配可以解决这些 问题
bean的修改,增加一个属性 autowire="byName"
<bean id="cat" class="com.iweb.pojo.Cat"/>
<bean id="cat2" class="com.iweb.pojo.Cat"/>
<bean id="dog" class="com.iweb.pojo.Dog"/>
<bean id="dog2" class="com.iweb.pojo.Dog"/>
<bean id="user" class="com.iweb.pojo.User" autowire="byName">
</bean>
使用的时候,bean中的property元素可以省略配置,spring会自动去帮你装配你所需要的对象
当容器中有多个相同对象,如cat、cat1 ,dog,dog1,这个时候都不会报错,只会去寻找有没有id为cat和id为dog的对象
byName总结:
当一个bean节点,带有atuowire byName属性时
1,将查找类中所有set方法名,比如setCat(),获得将set去掉并且首字母小写的字符串,即cat
2,去spring容器中寻找是否有此字符串名称id的对象
3,如果有,取出注入,没有,报空指针异常
byType
按照类型自动装配
使用byType要保证:同一个类型的对象,在spring容器中唯一,如果不唯一,会报一个不唯一的异常
beans.xml中的使用
<bean id="cat" class="com.iweb.pojo.Cat"/>
<bean id="dog" class="com.iweb.pojo.Dog"/>
<bean id="dog1" class="com.iweb.pojo.Dog"/>
<bean id="user" class="com.iweb.pojo.User" autowire="byType">
</bean>
使用的时候,在bean元素中,加一个auto=byType即可使用,使用的时候,如果容器中有多个相同的对象,则会报错。如以上中,多了一个dog1,就是错误的
使用注解
jdk1.5支持注解、spring从2.5支持注解
准备工作:
1,spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context/spring-context.xsd">
2,开启属性注解支持
<context:annotation-config/>
1,@Autowired
@Autowried是按类型自动装配的 ,不支持id匹配, 需要导入一个spring-aop的包
使用1,
可去掉set方法,直接在属性上加上注解
2,xml中配置修改为如下
3,测试,结果正常输出
使用方式:
直接在属性上使用或者所在set方法中使用
使用此注解可以不用编写set方法,前提是自动装配的属性在IOC容器中,且符合byType的规范
@autowired(required = false) 值是false,表示对象可以为null,如果为true,则必须存在对象
2,@Qualifier
@autowired是根据byType自动装配,@Qualifier是根据byName的方式自动装配
@Qualifier不能单独使用,配合@autowired一起用
步骤:
修改beans.xml 加上cat2合dog2
修改实体类文件
3、@Resource
@Resource如果指定了name属性,先按该属性进行byName方式查找装配,其次再进行byType
如果都不成功,则报异常
beans.xml
结论:先进行byName查找,失败了,进行byType查找
@autowired和@Resource 区别:
1,都可以用来装配bean,都可以写在字段上,或者set方法上
2,autowired默认按byType装备,默认情况下,必须要求依赖对象存在。如果要允许null值,可以设置required值为false。如果要使用byName装备需要使用 @Qualifier注解
3,@Resource 默认是按照byName装配,名称可以通过name属性指定,如果没指定name属性,当注解使用时,默认取字段名进行匹配,找不到匹配bean才按照类型装配
使用注解开发@Component
bean的实现 @Component
1,配置扫描哪些包下的注解
<!-- 配置扫描哪些包下的注解-->
<context:component-scan base-package="com.iweb.pojo"/>
2,再指定的包下,编写类,增加注解
3,测试
属性的注入 @Value
1.不用提供set方法,直接再名字上添加@value("值")
如果提供了set方法,可以再set方法上添加@value("值")
衍生注解
@Component 三个衍生的注解
@Controller : web层
@Servcie : service层
@Repository : dao层
使用注解注入访问对象
作用域注解 @Scope
直接使用在类上,跟之前使用的作用域取值相同
总结:
xml和注解的比较:
xml可以适用于任何场景,结构清晰,维护方便
注解不是自己提供的类是用不了,开发简单方便
xml和注解配合
xml管理bean
注解完成属性注入
基于java类进行配置
@Configuration
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
注意:@Configuration注解的配置类有如下要求:
-
-
- @Configuration不可以是final类型;
- @Configuration不可以是匿名类;
- 嵌套的configuration必须是静态类。
-
@Configuration加载Spring方法
1、@Configuration配置spring并启动spring容器
@Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)
新建一个模块,建包,不用配置beans.xml
写一个类TestConfiguration
@Configuration
public class TestConfiguration {
public TestConfiguration() {
System.out.println("TestConfiguration容器启动初始化。。。");
}
}
这个注解,相当于之前配置的beans.xml中的 <beans>标签
Test方法中测试
@Test
public void test02(){
// @Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext
ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class);
}
结果
2、@Configuration启动容器+@Bean注册Bean,@Bean下管理bean的生命周期
@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的<bean>,作用为:注册bean对象
Bean类
public class TestBean {
private String username;
private String url;
private String password;
public void sayHello() {
System.out.println("TestBean sayHello...");
}
public String toString() {
return "username:" + this.username + ",url:" + this.url + ",password:" + this.password;
}
public void start() {
System.out.println("TestBean 初始化。。。");
}
public void cleanUp() {
System.out.println("TestBean 销毁。。。");
}
}
config配置类中加入获取bean方法
测试bean获取调用方法输出
@Bean下管理bean的生命周期
可以使用基于 Java 的配置来管理 bean 的生命周期。
@Bean 支持两种属性,即 initMethod 和destroyMethod,这些属性可用于定义生命周期方法。在实例化 bean 或即将销毁它时,容器便可调用生命周期方法。
生命周期方法也称为回调方法,因为它将由容器调用。
测试
修改配置类
测试类中,把bean中的toString方法注释掉
结果
spring中bean的scope属性,有如下5种类型:
singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例
prototype表示每次获得bean都会生成一个新的对象
request表示在一次http请求内有效(只适用于web应用)
session表示在一个用户会话内有效(只适用于web应用)
globalSession表示在全局会话内有效(只适用于web应用)
在多数情况,我们只会使用singleton和prototype两种scope,如果在spring配置文件内未指定scope属性,默认为singleton。
3、@Configuration启动容器+@Component注册Bean
bean类
//添加注册bean的注解
@Component
public class TestBean {
private String username;
private String url;
private String password;
public void sayHello() {
System.out.println("TestBean sayHello...");
}
public String toString() {
return "username:" + this.username + ",url:" + this.url + ",password:" + this.password;
}
public void start() {
System.out.println("TestBean 初始化。。。");
}
public void cleanUp() {
System.out.println("TestBean 销毁。。。");
}
}
配置类
测试类
4,合并其他配置文件
TestConfig
MyConfig
实体类Dog
test方法测试
创建一个Person类,类中有一个Address类,作为属性,还有一个name属性
分别使用 xml、注解、配置类 三种方式,去装配Person类的bean对象,要求Person类的address属性也要注入
获取之后,分别输出person对象
Spring AOP
AOP面向切面编程
通过预编译的方式和运行动态代理实现程序功能的统一维护的一种技术
AOP是OOP的一种延续
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高了开发效率
底层机制就是动态代理模式
AOP实现步骤(注解版)
导入依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
1,创建项目,编写xml配置文件,开启AOP注解支持
使用aop:aspectj-autoproxy标签
<?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"
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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.iweb.spring"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
2,创建切面类
创建一个类,在类上加上@component和@Aspect
使用@Pointcut注解来指定要被增强的方法
使用@Before注解来给我们的增强代码所在的方法进行标识,并且指定了增强代码是在被增强方法执行之前执行的。
3,编写业务类,完成测试
执行结果
AOP核心概念
Joinpoint(连接点)︰所谓连接点是指那些可以被增强到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
service中或者dao中的方法都可以看成是连接点
Pointcut(切入点)︰所谓切入点是指被增强的连接点(方法)
Advice(通知/增强)︰所谓通知是指具体增强的代码
Target(目标对象)︰被增强的对象就是目标对象
案例中被增强的方法的对象
Aspect(切面)︰是切入点和通知(引介)的结合
切点和通知组成的类
Proxy(代理):一个类被AOP增强后,就产生一个结果代理类
增强的时候,其实spring帮助我们创建一个代理类,来实现增强效果
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能
Spring中AOP配置知识点
切点配置
切点表达式
可以使用切点表达式来表示要对哪些方法进行增强。
写法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号*代表任意
- 包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类·参数列表可以使用两个点..表示任意个数,任意类型的参数列表
举例
execution(* com.iweb.service.*.*(..))
表示com . iweb.service包下任意类,方法名任意,参数列表任意,返回值类型任意
execution(* com.iweb.service..*.*(..))
表示com. iweb.service包及其子包下任意类,方法名任意,参数列表任意,返回值类型任意
execution(* com .iweb.service.*.*())
表示com .iweb.service包下任意类,方法名任意,要求方法不能有参数,返回值类型任意
execution(* com. iweb.service.*.delete* ( ..))
表示com.iweb.service包下任意类,要求方法不能有参数,返回值类型任意,方法名要求已delete开头
切点函数@annotation
我们也可以在要增强的方法上加上注解。
然后使用@annotation来表示对加了什么注解的方法进行增强。
写法:@annotation(注解的全类名)
实现步骤
1,编写注解:
2,给需要增强的方法,添加自定义的注解
3,到切面类中,使用@annotation确定需要增强的方法
4,正常测试方法输出即可
通知分类
- @Before:前置通知,在方法执行前执行
- @AfterReturning:返回后通知,在目标方法执行后执行,如果出现异常不会执行
- @After:后置通知,在目标方法返回结果之后执行,无论是否出现异常都会执行
- @AfterThrowing:异常通知,在目标方法抛出异常后执行
- @Around:环绕通知,围绕着方法执行
public object test(){
before(); //@Before前置通知
try {
object ret =目标方法();//目标方法调用
afterReturing(); //@AfterReturning返回后通知
catch (Throwab1e throwab1e) {
throwab1e.printstackTrace();
afterThrowing(); / /@AfterThrowing异常通知通知
}finally{
after(); //@After异常通知通知
}
return ret;
}
注解方式获取增强方法对应的参数信息
我们可以在除了环绕通知外的所有通知方法中增加一个JoinPoint类型的参数。
这个参数封装了被增强方法的相关信息。我们可以通过这个参数获取到除了异常对象和返回值之外的所有信息。
使用方式:
在增强方法中传入参数:JoinPoint
joinPoint.getArgs() 获取传入参数
joinPoint.getTarget() 获取目标对象
joinPoint.getSignature() 获取对象签名信息
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); 获取被增强方法签名封装对象
Method method = signature.getMethod(); 获取到方法的Method对象
String name = signature.getName();
String declaringTypeName = signature.getDeclaringTypeName();
配置方式实现AOP
先写一个业务接口,正常的增删改查功能
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
写一个实现类
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 search() {
System.out.println("查询用户");
}
}
新建前置增强功能类和后置增强功能类
//前置增强
public class Log implements MethodBeforeAdvice {
//method: 表示要执行的目标对象的方法
//objects:被调用的方法的参数
//o :表示目标对象
//这个方法表示希望我们将来在执行某个业务中方法之前,输出一条日志信息,
//信息内容为,XXX的xxx方法被执行了
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
//后置增强方法
public class AfterLog implements AfterReturningAdvice {
//returnValue :返回值
//method 被调用的方法
//args 被调用的方法的对象的参数
//target 被调用的目标对象
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+target.getClass().getName()+"的"+
method.getName()+"方法,"+"返回值是:"+returnValue);
}
}
beans.xml配置,引入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
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.iweb.service.UserServiceImpl"/>
<bean id="log" class="com.iweb.log.Log"/>
<bean id="afterLog" class="com.iweb.log.AfterLog"/>
<!-- 配置aop-->
<aop:config>
<!-- 切入点 expression:表示表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.iweb.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) context.getBean("userService");
userService.search();
}
}
SpringAop就是将公共的业务(日志、安全等)和我们正常业务结合起来,当执行正常业务的时候,会把公共业务加进来,实现公共业务的重复利用,这样,正常业务更加纯粹,程序员专注于正常业务
整合mybatis
mybatis步骤
1,导入jar包
junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
mybatis
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
mysql-connector
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
spring相关
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.5</version>
</dependency>
aspectJ AOP织入器
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
mybatis-spring整合包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
配置maven静态资源过滤问题
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
项目结构
配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--别名,表示将pojo包下的User对象类型,简写为user-->
<typeAliases>
<package name="com.iweb.pojo"/>
</typeAliases>
<mappers>
<package name="com.iweb.mapper"/>
</mappers>
</configuration>
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: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">
<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=GMT%2B8&useSSL=false&useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--关联mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 注册sqlSessionTemplate,关联到sqlsessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<context:component-scan base-package="com.iweb"/>
</beans>
Spring事务管理
事务特性 ACID
原子性 :事务是由一系列动作组成,原子性可以保证动作要么全部完成,要么全部不完成。
一致性:一旦事务完成,事务被提交后,相关数据前后保持一致。
隔离性:多个事务之间可能会处理相同的数据,它们之间是相互隔离的
持久性:事务一旦完成,不管发生什么错误,结果不会收到影响,一般事务会持久化到存储器中
事务在企业开发中,是程序开发的必备技术,用来确保数据的完整性和一致性
Spring中的事务管理
编程式事务管理
将事务管理代码嵌入到业务方法中来控制事务的提交回滚
缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码
声明式事务管理
一般比编程式好用点
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理
将事务管理作为横切关注点,通过aop方法模块化,在spring中通过Spring AOP 框架支持声明式事务管理
先测试不加事务的时候
UserMapper接口
在这个类中,查询用户的时候中间加了一个增加方法,和删除方法
测试
测试的时候发现,如果在代码中加入异常
但是执行的时候,增加方法仍然能成功添加数据,这种情况是不合理的,因为增加和删除在同一个事务中。
基于注解的方式实现事务
<!--配置事务-->
<bean id="tsManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="tsManager"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
在对应的方法上加上注解 @Transactional
即可实现事务的加入
注意事项:
1,@Transactional 可以加在类上或者方法上,加到类上后,类中的所有方法都会受到事务控制,加到方法上就只有方法受到影响
2,@Transactional将来一般加载Service层中
基于Spring AOP实现事务管理
使用Spring管理事务,
先引入xml约束:tx
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
配置事务
<!-- 事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>
<!-- 配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 配置哪些方法使用什么样的事务,配置事务传播特性-->
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED" />
<tx:method name="update" propagation="REQUIRED" />
<tx:method name="search" propagation="REQUIRED" />
<tx:method name="get" read-only="true" />
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- 配置AOP-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.iweb.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>