目录
1、Spring
1.1 简介
春天 —>给软件行业带来了春天
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术
官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency>
1.2 优点
Spring是一个开源免费的框架 (容器)!
Spring是一个轻量级的框架 , 非侵入式的
控制反转 IoC , 面向切面 Aop
对事务的支持 , 对框架整合的支持
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
1.3 组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .
!(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210607123115699.png)
核心容器:核心容器提供 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.4 拓展
如何学习现代java
Spring Boot 一个快速开发的脚手架 基于SpringBoot可以快速的开发单个微服务
约定大于配置
Spring Cloud Spring Cloud是基于SpringBoot实现的
发展太久的弊端:到后期配置十分繁琐,人称配置地狱
2、IOC理论推导
UserDao 接口 public interface UserDao { public void getUser(); }
UserDaoImpl 实现类 public class UserDaoImpl implements UserDao { @Override public void getUser() { System.out.println("获取用户数据"); } }
UserService 业务接口 public interface UserService { public void getUser(); }
UserServiceImpl 业务实现类 public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); @Override public void getUser() { userDao.getUser(); } }
测试一下
@Test public void test(){ UserService service = new UserServiceImpl(); service.getUser(); }
把Userdao的实现类增加一个 .
public class UserDaoMySqlImpl implements UserDao { @Override public void getUser() { System.out.println("MySql获取用户数据"); } }
紧接着我们要去使用MySql的话 , 我们就需要去service实现类里面修改对应的实现
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoMySqlImpl(); @Override public void getUser() { userDao.getUser(); } }
代码量十分大,修改一次的成本十分昂贵!
我们使用一个Set接口实现,已经发生了革命性的变化!
从 你给什么我用什么 ===》 我要什么你给什么
public class UserServiceImpl implements UserService { private UserDao userDao; // 利用set实现 public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void getUser() { userDao.getUser(); } }
之前,程序是主动创建对象,控制权在程序员手上,修改程序的代价十分昂贵! 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象! 这种思想,从本质上解决了问题,我们程序员不用再去管对象的创建了。系统的耦合性大大降低,可以专注在业务的实现上!这是IOC的原型!
IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3、HelloSpring
导入Jar包
注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.10.RELEASE</version> </dependency>
编写代码
1、编写一个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 ); } }
2、编写我们的spring文件 , 这里我们命名为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就是java对象 , 由Spring创建和管理--> <bean id="hello" class="com.luo.pojo.hello"> <property name="name" value="Spring"/> </bean> </beans>
3、我们可以去进行测试了 .
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.luo.pojo.Hello; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数即为spring配置文件中bean的id . Hello hello = (Hello) context.getBean("hello"); hello.show(); } }
思考
-
Hello 对象是谁创建的 ?
hello 对象是由Spring创建的
-
Hello 对象的属性是怎么设置的 ?
hello 对象的属性是由Spring容器设置的
这个过程就叫控制反转 :
-
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
-
反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
依赖注入 : 就是利用set方法来进行注入的.
-
IOC是一种编程思想,由主动的编程变成被动的接收
-
所谓的IOC控制翻转就是对象由Spring创建,管理,装配
可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .
配置文件模板
<?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"> <!-- services --> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="itemDao" ref="itemDao"/> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for services go here --> </beans>
修改案例一
我们在案例一中, 新增一个Spring配置文件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"> <!-- services --> <bean id="MysqlImpl" class="com.luo.dao.UserDaoMysqlImpl"></bean> <bean id="Impl" class="com.luo.dao.UserDaoImpl"></bean> <bean id="UserSevbiceImpl" class="com.luo.service.UserSevbiceImpl"> <property name="userDao" ref="Impl"/> <!-- additional collaborators and configuration for this bean go here --> </bean> <!-- more bean definitions for services go here --> </beans>
测试!
import com.luo.service.UserSevbiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { //获取applicationcontext,拿到spring的容器 ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); UserSevbiceImpl userSevbiceImpl= (UserSevbiceImpl) context.getBean("UserSevbiceImpl"); userSevbiceImpl.getUser(); } }
OK , 到了现在 , 我们彻底不用再程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
4、IOC创建对象的方式
使用无参构造创建对象,默认!
假设我们要使用有参构造创建对象。
下标赋值
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
<bean id="user" class="com.luo.pojo.User"> <constructor-arg index="0" value="lsw"/> </bean>
构造参数类型
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
<bean id="user" class="com.luo.pojo.User"> <constructor-arg type="java.lang.String" value="lsw"/> </bean>
构造参数名
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
<bean id="user" class="com.luo.pojo.User"> <constructor-arg name="name" value="lsw"/> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
5、Spring配置
5.1 别名
<!--设置别名:在获取Bean的时候可以使用别名获取--> <alias name="userT" alias="userNew"/>
name也是取别名,且可以同时取多个
<bean id="userT" class="com.luo.pojo.UserT" name="t">
5.2 Bean的配置
<!--bean就是java对象,由Spring创建和管理--> <!-- id : bean的标识符,要唯一,如果没有配置id,name就是默认标识符 如果配置id,又配置了name,那么name是别名 name可以设置多个别名,可以用逗号,分号,空格隔开 如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象; class : bean的全限定名=包名+类名 --> <bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean>
5.3 import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个;
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!
<import resource="{path}/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"> <import resource="beans.xml"/> </beans>
6、DI依赖注入
6.1 构造器注入
前面已经说过了
6.2 Set方式注入 【重点】
依赖注入的本质:Set注入 依赖:bean对象的创建依赖于容器 注入:bean对象中的所有属性,由容器来注入 模拟环境搭建
两个实体类
package com.luo.pojo; import java.util.*; 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 wife; private Properties info; 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 Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } 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 Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }
package com.luo.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
配置 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"> <bean id="address" class="com.luo.pojo.Address"> <property name="address" value="四川省成都市"/> </bean> <bean id="student" class="com.luo.pojo.Student"> <!-- 第一种,普通值注入,value--> <property name="name" value="罗思维"/> <!-- 第二种ref注入--> <property name="address" ref="address"/> <!-- 第三种,数组--> <property name="books"> <array> <value>红楼梦</value> <value>三国演义</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="123456"/> <entry key="银行卡" value="123456789"/> <entry key="学生证" value="1806011114"/> </map> </property> <!-- 第六种--> <property name="games"> <set> <value>LOL</value> <value>WOW</value> <value>COC</value> </set> </property> <!-- 第七种null--> <property name="wife"> <null/> </property> <!-- <property name="wife" value="null"/> --> <!-- 第八种,properties--> <property name="info"> <props> <prop key="学号">1806011114</prop> <prop key="邮箱">1806011114@qq.com</prop> <prop key="姓名">付浪</prop> </props> </property> </bean> </beans>
测试
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString); } }
6.3 拓展方式注入
我们可以使用p命名空间和c命名空间进行注入
官方解释:
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.kuang.pojo.User" p:name="狂神" p:age="18"/> <!--c命名空间注入,通过构造器注入:construt-args--> <bean id="user2" class="com.kuang.pojo.User" c:name="狂神2" c:age="11"/> </beans>
注意点:p命名和c命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
6.4 Bean的作用域
单例模式(Spring默认机制)
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神2" c:age="11" scope="singleton"/>
原型模式:每次从容器中get的时候,都会产生一个新对象
<bean id="user2" class="com.kuang.pojo.User" c:name="狂神2" c:age="11" scope="prototype"/>
其余的request、session、application这些只能在web开发中使用到
7、Bean的自动装配
自动装配是Spring满足bean依赖的一种方式 Spring会在上下文中自动寻找,并自动给bean装配属性 在Spring中有三种装配的方式
在xml中显示的配置 在java中显示的配置 隐式的自动装配bean 【重要】
7.1 测试
环境搭建:一个人有两个宠物
7.2 byName自定装配
<bean id="cat" class="com.kuang.pojo.Cat"/> <bean id="dog" class="com.kuang.pojo.Dog"/> <!-- byName : 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean_id --> <bean id="people" class="com.kuang.pojo.People" autowire="byName"> <property name="name" value="狂神"/> </bean>
7.3 byTpye自动装配
<!-- byName : 会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean_id byType : 会自动在容器上下文中查找,和自己对象属性类型相同的bean --> <bean id="people" class="com.kuang.pojo.People" autowire="byType"> <property name="name" value="狂神"/> </bean>
小结:
byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致 byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
7.4 使用注解实现自动装配
jdk1.5支持的注解,Spring2.5就支持注解了!
要使用注解须知:
导入约束 配置注解的支持 <context:annotation-config/>
<?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:annotation-config/> </beans>
public @interface Autowired { boolean required() default true; }
直接在属性上使用即可,也可以在set方法上使用
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在 IOC(Spring)容器中存在,且当你的类型唯一时符合类型byType,当你的类型不唯一时符合类型byName
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null;
测试代码:
public class People { //如果显示定义了Autowired的required属性为false,说明这个对象可以为Null,否则不允许为空 @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name; }
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value = "xxx")去配合@Autowire的使用,指定一个唯一的bean对象注入!
public class People { @Autowired @Qualifier(value = "cat2") private Cat cat; @Autowired private Dog dog; private String name; }
@Resource public class People { @Resource( name = "cat3") private Cat cat; @Resource private Dog dog; private String name; }
小结:
@Resource和@Autowired的区别: 都是用来自动转配的,都可以放在属性字段上 @Autowired 是通过byType的方式实现,而且必须要求这个对象存在!【常用】 @Resource 默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】 执行顺序不同: @Autowired 通过byType的方式实现。@Resource默认通过byName的方式实现。 入! public class People { @Autowired @Qualifier(value = "cat2") private Cat cat; @Autowired private Dog dog; private String name; }
@Resource public class People { @Resource( name = "cat3") private Cat cat; @Resource private Dog dog; private String name; }
小结:
@Resource和@Autowired的区别:
都是用来自动转配的,都可以放在属性字段上 @Autowired 是通过byType的方式实现,而且必须要求这个对象存在!【常用】 @Resource 默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】 执行顺序不同: @Autowired 通过byType的方式实现。@Resource默认通过byName的方式实现。
8 使用注解开发
8.1 过程
8.1.1 导包
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
8.1.2 配置
<?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:annotation-config/> <!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="pojo"/> </beans>
8.1.3 属性如何注入
import org.springframework.stereotype.Component; @Component public class Cat { @Value("猫咪") private String name; @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
8.2 衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层。这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean dao 【@Repository】 service 【@Service】 controller 【@Controller】
8.3 自动装配
见 7 Bean的自动装配
8.4 作用域
@Scope(“singleton”) package pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("singleton") public class Cat { @Value("猫咪") private String name; @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
8.5 小结
xml与注解: xml更加万能,适用于任何场合!维护简单方便 注解不是自己类使用不了,维护相队复杂! xml与注解最佳实践: xml用来管理bean; 注解只负责完成属性的注入; 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持。
9 使用Java的方式配置Spring
9.1 简介
我们现在要完全不使用Spring的xml配置了,全权交给Java来做! JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
这种纯Java的配置方式,在SpringBoot中随处可见!
9.2 实现
实体类 不需要加@Component @Autowired还是需要的
package pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; public class Cat { @Value("猫咪") private String name; @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
package pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; public class Dog { @Value("狗砸") private String name; @Override public String toString() { return "Dog{" + "name='" + name + '\'' + '}'; } }
package pojo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; public class Person { @Autowired private Dog dog; @Autowired private Cat cat; @Override public String toString() { return "Person{" + "dog=" + dog + ", cat=" + cat + '}'; } }
java配置文件 配置文件名可以任意取 类上要加@Configuration 方法上要加@Bean 方法名就是bean的名字
package config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import pojo.Cat; import pojo.Dog; import pojo.Person; @Configuration public class Myconfig { @Bean public Dog dog(){ return new Dog(); } @Bean public Cat cat(){ return new Cat(); } @Bean public Person getPerson(){ return new Person(); } }
测试类
package pojo; import config.Myconfig; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class PersonTest { @Test public void personTest(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Myconfig.class); Person person = (Person) applicationContext.getBean("getPerson"); System.out.println(person); } }
10 代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式的分类:
静态代理 动态代理
10.1 静态代理
角色分析: 抽象角色:一般会使用接口或者抽象类来解决(租房)
package com.luo.demo01; public interface Rent { public void rent(); }
真实角色:被代理的角色(房东)
package com.luo.demo01; public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子"); } }
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作(中介)
package com.luo.demo01; public class Proxy { private Host host; public Proxy(){ } public Proxy(Host host) { this.host = host; } public void rent(){ host.rent(); } public void see(){ System.out.println("中介带你看房"); } }
客户:访问代理对象的人!(租房者)
package com.luo.demo01; public class Client { public static void main(String[] args) { Host host=new Host(); Proxy proxy=new Proxy(host); proxy.rent(); proxy.see(); } }
代理模式的好处:
1.使真实的角色可以专注业务,不用关注公共事务
2.公共事务就交给了代理角色,实现了业务分工
3.公共业务拓展时,方便集中管理
缺点:一个真实角色就需要一个代理角色
Demo2
抽象对象
package com.luo.demo02; public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
真实对象
package com.luo.demo02; 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 query() { System.out.println("查询了一个用户"); } }
代理对象
package com.luo.demo02; public class UserServiceProxy implements UserService{ private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } public UserServiceProxy(){ } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } public void log(String msg){ System.out.println("使用了"+msg+"方法"); } }
用户
package com.luo.demo02; public class Client { public static void main(String[] args) { UserServiceImpl userService=new UserServiceImpl(); UserServiceProxy proxy=new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); proxy.delete(); } }
10.2 动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的!
动态代理分为两大类:基于接口的动态代理,基于类的动态代理 1.基于接口 — JDK动态代理【我们在这里使用】 2.基于类:cglib java字节码实现:JAVAssist
需要了解两个类:
Proxy:代理;InvocationHandler:调用处理程序。
10.2.1 JDK动态代理实现
被代理的接口
package com.luo.demo03; public interface Rent { public void rent(); }
被代理的接口的一个实现类
package com.luo.demo03; public class Host implements Rent { @Override public void rent() { System.out.println("房东要出租房子"); } }
ProxyInvocationHandler类
package com.luo.demo03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyIn 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); } //在代理对象调用方法时,通过反射调用方法。适用于该接口的所有方法。在这个方法里所作的增强动作将应用于该接口的所有方法。 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { see(); Object result = method.invoke(rent, args); return result; } public void see(){ System.out.println("带看房子"); } }
测试类
package com.luo.demo03; public class Client { public static void main(String[] args) { Host host=new Host(); ProxyIn pih = new ProxyIn(); pih.setRent(host); Rent proxy= (Rent) pih.getProxy();//这里的proxy就是动态生成 proxy.rent(); } }
10.2.2 JDK动态代理工具类
动态代理工具类
package pojo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandlerUtil implements InvocationHandler { private Object target; public void setObject(Object object) { this.object = target; } public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { fun1(); Object invoke = method.invoke(object, args); fun2(); return invoke; } public void fun1(){ System.out.println("增强方法一"); } public void fun2(){ System.out.println("增强方法二"); } }
测试
@Test public void test(){ ProxyInvocationHandlerUtil pih = new ProxyInvocationHandlerUtil(); pih.setObject(cat); Animal animal = (Animal) pih.getProxy(); animal.say(); animal.eat(); }
11 AOP
11.1 什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用。
11.2 AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等… 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。 目标(Target):被通知对象。 代理(Proxy):向目标对象应用通知之后创建的对象。 切入点(PointCut):切面通知执行的“地点”的定义。
-
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即AOP在不改变原有代码的情况下,去增加新的功能。
11.3 使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包!
11.3.1 使用Spring的API接口实现
1.导依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> <dependency>
2.编写接口和实现类
package com.luo.service; public interface UserService { public void add(); public void delete(); public void update(); public void select(); }
package com.luo.service; import static java.lang.System.*; public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete(){ System.out.println("删除了一个用户"); } public void update(){ System.out.println("修改了一个用户"); } public void select() { System.out.println("查询了一个用户"); } }
3.编写aop增强类
package com.luo.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; 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()+"被执行了"); } }
package com.luo.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; 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); } }
4.去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束,配置bean的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 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.luo.service.UserServiceImpl"/> <bean id="log" class="com.luo.log.Log"/> <bean id="afterLog" class="com.luo.log.AfterLog"/> <aop:config> <aop:pointcut id="pointcut" expression="execution(* com.luo.service.UserServiceImpl.*(..))"/> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
5.测试
import com.luo.service.UserService; import com.luo.service.UserServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("userService"); userService.add(); } }
11.3.2 自定义类来实现AOP
execution()是最常用的切点函数,其语法如下所示:
整个表达式可以分为五个部分:
1、execution(): 表达式主体。
2、第一个*号:表示返回类型, *号表示所有的类型。
3、包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.sample.service.impl包、子孙包下所有类的方法。
4、第二个*号:表示类名,
*号表示所有的类。
5、(..):最后这个星号表示方法名,*号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数
execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?) 除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。与其直接讲解该方法的使用规则,还不如通过一个个具体的例子进行理解。
1.导依赖包 2.编写接口和实现类 3.编写自定义增强类
package com.luo.diy; public class DiyPointCut { public void before(){ System.out.println("方法执行前"); } public void after(){ System.out.println("方法执行后"); } }
4.去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束,配置bean的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" 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 id="userService" class="com.luo.service.UserServiceImpl"/> <bean id="log" class="com.luo.log.Log"/> <bean id="afterLog" class="com.luo.log.AfterLog"/> <bean id="diy" class="com.luo.diy.DiyPointCut"/> <aop:config> <aop:aspect ref="diy"> <aop:pointcut id="point" expression="execution(* com.luo.service.UserServiceImpl.*(..))"/> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config> </beans>
5.测试
import com.luo.service.UserService; import com.luo.service.UserServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("userService"); userService.add(); } }
11.3.3 使用注解实现AOP
使用环绕时,环绕在before和after前执行
1.导依赖包 2.编写接口和实现类 3.定义注解实现的AnnotationPointCut增强类
package com.luo.diy; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect//标注这个类是个切面 public class AnnotationPointCut { @Before("execution(* com.luo.service.UserServiceImpl.*(..))") public void before(){ System.out.println("方法执行前2"); } @After("execution(* com.luo.service.UserServiceImpl.*(..))") public void after(){ System.out.println("方法执行后2"); } }
4.在Spring配置文件中,注册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" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userService" class="com.luo.service.UserServiceImpl"/> <bean id="log" class="com.luo.log.Log"/> <bean id="afterLog" class="com.luo.log.AfterLog"/> <bean id="diy" class="com.luo.diy.DiyPointCut"/> <bean id="Anno" class="com.luo.diy.AnnotationPointCut"/> <aop:aspectj-autoproxy/> <!-- <aop:config> <aop:aspect ref="diy"> <aop:pointcut id="point" expression="execution(* com.luo.service.UserServiceImpl.*(..))"/> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>--> </beans>
5.测试
import com.luo.service.UserService; import com.luo.service.UserServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("userService"); userService.add(); } }
12.1 步骤: 1.导入相关jar包 junit mybatis mysql数据库 spring相关 aop织入器 mybatis-spring整合包【重点】在此还导入了lombok包。 配置Maven静态资源过滤问题!
<dependencies> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> ``` <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.4</version> </dependency> <!--Spring操作数据库的话,还需要一个spring-jdbc --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.4</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.18</version> <scope>provided</scope> </dependency> </dependencies> 1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 2.编写配置文件 3.测试 12.2 回顾Mybatis 编写pojo实体类 编写实现mybatis的配置文件 编写UserMapper接口 编写UserMapper.xml文件 测试
12.3 Mybatis-Spring 什么是MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
文档链接:http://mybatis.org/spring/zh/index.html
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency>
1 2 3 4 5 12.3.1 整合实现一 引入Spring配置文件spring-mybatis.xml(名字自取) <?xml version="1.0" encoding="GBK"?> <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> 1 2 3 4 5 6 7 配置数据源替换mybaits的数据源。这里数据源的具体信息还是从属性文件读取的,要加红框里的信息。测试时遇到用户名属性文件配置成username并读取username就连不上数据库,改成别的就可以。最后改成user做了连接。
配置SqlSessionFactory,关联MyBatis
注册sqlSessionTemplate,关联sqlSessionFactory
需要UserMapper接口的UserMapperImpl 实现类,私有化sqlSessionTemplate package dao;
import org.mybatis.spring.SqlSessionTemplate; import pojo.User;
public class UserMapperImpl implements UserMapper{ //我们的所有操作,都使用sqlSession来执行,在原来,现在都使用SqlsessionTemplate private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public User getUserById(int id) { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.getUserById(id); }
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 将自己写的实现类,注入到Spring配置文件中。
整个spring-mybatis.xml代码如下 <?xml version="1.0" encoding="GBK"?> <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:property-placeholder location="classpath:db.properties"/> <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid 我们这里使用Spring提供的JDBC:--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/>
</bean> <!--sqlSessionFactory--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--关联mybatis配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <!--SqlSessionTemplate:就是我们使用的sqlSession--> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <!--只能使用构造器注入sqlSessionFactory,因为它没有set方法--> <constructor-arg index="0" ref="sqlSessionFactory" /> </bean> <bean id="userMapper" class="dao.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean>
</beans> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 测试 @Test public void test () {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml"); UserMapper userMapper = (UserMapper) context.getBean("userMapper"); System.out.println(userMapper.getUserById(1)); }
1 2 3 4 5 6 7 8 12.3.2 整合一小结 整合一的关键是spring-mybatis.xml文件(这个文件名自己取,只要和测试时的ApplicationContext context = new ClassPathXmlApplicationContext("spring-mybatis.xml");文件名匹配上就可以了)。 spring-mybatis.xml文件里配置了数据源 所以原mybatis的核心配置文件里的environments就可以去掉。 spring-mybatis.xml文件里配置了sqlSessionFactory,用的是spring提供的SqlSessionFactoryBean。
所以原mybatis的获取SqlSession的类可以去掉。 建议Mappe.xml的配置还是放在mybatis核心 配置文件中
不要配在下图红框下面,测试时试过<property name="mapperLocations" value="classpath:dao/*.xml"/>通配写法报找不到文件<property name="mapperLocations" value="classpath:dao/UserMapper.xml"/>就可以。
12.3.3 整合实现二 mybatis-spring1.2.3版以上的才有这个,官方文档截图:
dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起整合方式一 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看。 将我们上面写的UserMapperImpl修改成UserMapperImpl1,重点是继承SqlSessionDaoSupport: package dao;
import org.mybatis.spring.support.SqlSessionDaoSupport; import pojo.User;
public class UserMapperImpl1 extends SqlSessionDaoSupport implements UserMapper{ public User getUserById(int id) { return getSqlSession().getMapper(UserMapper.class).getUserById(1); } }
1 2 3 4 5 6 7 8 9 10 11 SqlSessionDaoSupport类有个sqlSessionFactory属性要在配置bean的时候注入。 <bean id="userMapper1" class="dao.UserMapperImpl1"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> 1 2 3 测试 @Test public void test3 () {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserMapper userMapper1 = (UserMapper) context.getBean("userMapper1"); System.out.println(userMapper1.getUserById(1)); }
———————————————— 版权声明:本文为优快云博主「cat_2_love_dog」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.youkuaiyun.com/cat_2_love_dog/article/details/115217818