本博文专用于软件创新实验室 Spring
框架课堂,由于课堂时间有限,选取了 Spring
框架中比较重点的几个来介绍。 Spring
框架的诞生是为了使开发更加高效简洁,同时减少耦合程度,主要还是思想上的一个转变,想要深入了解 Spring
框架的,可以查阅 Spring 官方文档,也推荐一下狂神老师的视频教学,值得一看。
2002年,Rod Jahnson首次推出了 Spring
框架雏形 interface21
框架,
2004年3月24日,Spring
框架以 interface21
框架为基础,经过重新设计,发布了1.0正式版,
Spring理念 : 使现有技术更加实用 , 本身就是一个大杂烩 , 整合现有的框架技术,
官网:spring.io/
官方下载地址:repo.spring.io/libs-releas…
GitHub:github.com/spring-proj…
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
Spring
框架是一个分层架构,由 7 个定义良好的模块组成。Spring
模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean
的方式 。
组成 Spring
框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器(Spring Core)
核心容器提供 Spring 框架的基本功能。Spring 以 bean 的方式组织和管理 Java 应用中的各个组件及其关系。Spring 使用 BeanFactory 来产生和管理 Bean,它是工厂模式的实现。BeanFactory 使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
应用上下文(Spring Context)
Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring面向切面编程(Spring AOP)
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
JDBC和DAO模块(Spring DAO)
JDBC、DAO 的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
对象实体映射(Spring ORM)
Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 对象的关系工具,其中包括了 Hibernate、JDO 和 IBatis SQL Map 等,所有这些都遵从 Spring 的通用事物和 DAO 异常层次结构。
Web模块(Spring Web)
Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以 Spring 框架支持与 Struts 集成,web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
MVC模块(Spring Web MVC)
MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的。MVC 容纳了大量视图技术,其中包括 JSP、POI 等,模型来有 JavaBean 来构成,存放于 m 当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由 c 的事情。Spring 框架的功能可以用在任何 J2EE 服务器当中,大多数功能也适用于不受管理的环境。Spring 的核心要点就是支持不绑定到特定 J2EE 服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的 J2EE 环境,独立应用程序和测试环境之间重用。
新建一个 Maven 项目,在 pom.xml
中进行配置,
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.3</version>
</dependency>
目录
先编写一个 User
实体类,User.java
,
package com.idiot.pojo;
public class User{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
再编写我们的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">
<!--使用Spring来创建对象,在Spring中这些都称为Bean-->
<!--bean就是java对象 , 由Spring创建和管理-->
<bean id="helloSpring" class="com.idiot.pojo.User">
<property name="name" value="Spring"/>
</bean>
</beans>
最后进行测试,MyTest.java
,
import com.idiot.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中管理,我们要使用的话,直接从里面取出来
User helloSpring = (User) context.getBean("helloSpring");
System.out.println(helloSpring.toString());
}
}
运行结果
如果添加了别名,我们也可以使用别名获取这个对象,原名也是可以使用的,在 beans.xml
中进行配置,
<alias name="helloSpring" alias="helloAlias"/>
<bean id="helloSpring" class="com.idiot.pojo.User">
<property name="name" value="Spring"/>
</bean>
bean
就是 java
对象,由 Spring
创建和管理,
id
:bean
的唯一标识符,也就相当于对象名,class
:bean
对象所对应的全限定名:包名+类型,name
:也是别名,而且 name
可以同时取多个别名,可以用逗号,分号,空格隔开,如果没有配置 id
,name
就是默认标识符,
如果配置了 id
,又配置了 name
,那么 name
是别名,
如果不配置 id
和 name
,可以根据 applicationContext.getBean(.class)
获取对象,
import
一般用于团队开发使用,它可以将多个配置文件导入合并成为一个,
假设现在项目中有多个人开发,其中三个人负责不同的类的开发,不同的类需要注册在不同的bean
配置文件中,我们可以利用import
将所有人的beans.xml
合并成一个总的,即applicationContext.xml
,
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
使用的时候直接使用总配置applicationContext.xml
即可。
User
对象是谁创建的?
User
对象是由Spring
创建的,
<bean id="helloSpring" class="com.idiot.pojo.User">
<property name="name" value="Spring"/>
</bean>
类型 变量名 = new 类型();
User user = new User();
id = 变量名
class = new的对象
property相当于给对象中的属性设值
User
对象的属性是怎么设置的?
User
对象的属性是由Spring
容器设置的,这个过程就叫控制反转:
Spring
后,对象是由Spring
来创建的,依赖注入:就是利用set
方法来进行注入的,
IOC
是一种编程思想,由主动的编程变成被动的接收,可以通过newClassPathXmlApplicationContext
去浏览一下底层源码,
控制反转IoC(Inversion of Control),是一种设计思想,是一种通过描述(XML
或注解)并通过第三方去生产或获取特定对象的方式,在 Spring
中实现控制反转的是 IoC
容器,其实现方法是依赖注入(Dependency Injection,DI), 也有人认为 DI 只是 IoC 的另一种说法。没有 IoC 的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC
是 Spring
框架的核心内容,使用多种方式完美的实现了 IoC
,可以使用 XML
配置,也可以使用注解,新版本的 Spring
也可以零配置实现 IoC
。
采用 XML
方式配置 Bean
的时候,Bean
的定义信息和实现是分离的,而采用注解的方式可以把两者合为一体,Bean
的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
首先创建一个Maven
项目,然后创建接口和实现类,目录总览如下,
UserDao.java
接口,
package com.idiot.dao;
public interface UserDao {
public void getUser();
}
Dao
的实现类,UserDaoImpl.java
,
package com.idiot.dao;
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
UserService
的接口,UserService.java
,
package com.idiot.service;
public interface UserService {
public void getUser();
}
Service
的实现类,UserServiceImpl.java
,
package com.idiot.service;
import com.idiot.dao.UserDao;
import com.idiot.dao.UserDaoImpl;
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
测试一下,MyTest.java
,
import com.idiot.service.UserService;
import com.idiot.service.UserServiceImpl;
import org.junit.Test;
public class MyTest {
@Test
public void test(){
//用户实际调用的是业务层,Dao层他们不需要接触!
UserService service = new UserServiceImpl();
service.getUser();
}
}
这里要使用 @Test
,需要导入 junit.jar
,
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
运行结果如下,
现在增加一个 Dao
的实现类,UserDaoOracleImpl.java
,
package com.idiot.dao;
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
如果用户想去调用 Oracle
这个实现类,则我们必须去源代码去修改代码,即在 service
实现类里面修改对应的实现,
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoOracleImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
再加一个 Mysql
的实现类也是一样的,UserDaoSqlserverImpl.java
,
package com.idiot.dao;
public class UserDaoSqlserverImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
现在我们这个程序的代码少,修改起来是不麻烦的,但假设修改需求非常大 , 修改源码这种方式就根本不适用了, 每次变动都需要修改大量代码,这样子的成本代价是十分昂贵的。
如何解决这样的问题?
我们将使用一个 set
接口实现,使之发生革命性的变化!
UserServiceImpl.java
package com.idiot.service;
import com.idiot.dao.UserDao;
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
MyTest.java
@Test
public void test(){
UserServiceImpl service = new UserServiceImpl();
//Mysql实现
service.setUserDao( new UserDaoSqlserverImpl() );
service.getUser();
//Oracle实现
service.setUserDao( new UserDaoOracleImpl() );
service.getUser();
}
与使用 set
前的代码进行对比,这已经发生了根本性的变化,以前所有东西都是由程序主动去进行控制创建 , 而使用了 set
注入之后,程序不再具有主动性,而是变成了被动接受的对象,即把主动权交给了调用者,程序则只负责提供一个接口,
这种思想,从本质上解决了问题,我们程序员不再去管理对象的创建,而是更多的去关注业务的实现,耦合性大大降低,这也就是IOC的原型!
目录
User.java
package com.idiot.pojo;
public class User {
private String name;
public User() {
System.out.println("User无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.idiot.pojo.User">
<property name="name" value="Hello Spring!"/>
</bean>
</beans>
MyTest.java
import com.idiot.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.show();
}
}
运行结果
此时,将无参构造改成有参构造再运行,
public User(String name) {
System.out.println("User无参构造");
}
发现程序报错,没有办法进行初始化,Bean
初始化失败,
有参构造器,
public User(String name) {
this.name = name;
}
<bean id="user" class="com.idiot.pojo.User">
<constructor-arg index="0" value="idiot"/>
</bean>
如果有两个及以上的相同类型,就无法判断把值给谁了,因此不建议使用!
<bean id="user" class="com.idiot.pojo.User">
<constructor-arg type="java.lang.String" value="idiot.."/>
</bean>
<bean id="user" class="com.idiot.pojo.User">
<constructor-arg name="name" value="Idiot"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
初始Spring的创建中已讲,忘了的话回头看一下。
set
注入, bean
对象的创建依赖于容器,bean
对象中的所有属性由容器来注入,【环境搭建】
Address.java
package com.idiot.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 + '\'' +
'}';
}
}
如果在 Address
类中不添加 toString
方法,则会导致输出的不是 String
值,而是类似于这样子的 com.idiot.pojo.Address@319b92f3
,
Student.java
package com.idiot.pojo;
import java.util.*;
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 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 Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
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 + '\'' +
", \naddress=" + address +
", \nbooks=" + Arrays.toString(books) +
", \nhobby=" + hobby +
", \ncard=" + card +
", \ngames=" + games +
", \nwife='" + wife + '\'' +
", \ninfo=" + info +
'}';
}
}
<bean id="student" class="com.idiot.pojo.Student">
<property name="name" value="idiot"/>
</bean>
注意:这里的值是一个引用,ref
,
<bean id="addr" class="com.idiot.pojo.Address">
<property name="address" value="浙江"/>
</bean>
<bean id="student" class="com.idiot.pojo.Student">
<property name="name" value="idiot"/>
<property name="address" ref="addr"/>
</bean>
<bean id="student" class="com.idiot.pojo.Student">
<property name="name" value="idiot"/>
<property name="address" ref="addr"/>
<property name="books">
<array>
<value>C++</value>
<value>Java</value>
<value>Python</value>
</array>
</property>
</bean>
<property name="hobby">
<list>
<value>sing</value>
<value>climb</value>
</list>
</property>
<property name="card">
<map>
<entry key="中国电信" value="10000"/>
<entry key="中国移动" value="10086"/>
</map>
</property>
<property name="games">
<set>
<value>堡垒之夜</value>
<value>皇室战争</value>
<value>王者荣耀</value>
</set>
</property>
<property name="wife"><null/></property>
<property name="info">
<props>
<prop key="学号">123456</prop>
<prop key="性别">man</prop>
<prop key="姓名">idiot</prop>
</props>
</property>
下面是完整的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="addr" class="com.idiot.pojo.Address">
<property name="address" value="浙江"/>
</bean>
<bean id="student" class="com.idiot.pojo.Student">
<property name="name" value="idiot"/>
<property name="address" ref="addr"/>
<property name="books">
<array>
<value>C++</value>
<value>Java</value>
<value>Python</value>
</array>
</property>
<property name="hobby">
<list>
<value>sing</value>
<value>climb</value>
</list>
</property>
<property name="card">
<map>
<entry key="中国电信" value="10000"/>
<entry key="中国移动" value="10086"/>
</map>
</property>
<property name="games">
<set>
<value>堡垒之夜</value>
<value>皇室战争</value>
<value>王者荣耀</value>
</set>
</property>
<property name="wife"><null/></property>
<property name="info">
<props>
<prop key="学号">123456</prop>
<prop key="性别">man</prop>
<prop key="姓名">idiot</prop>
</props>
</property>
</bean>
</beans>
以及测试MyTest.java
,
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
}
运行结果
需要在头文件中加入约束文件xmlns:p="http://www.springframework.org/schema/p"
,且实体类中要存在无参构造器,
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="userP" class="com.idiot.pojo.User" p:name="idiot" p:age="3"/>
MyTest.java
@Test
public void test02(){
ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
User user = context.getBean("userP", User.class);
System.out.println(user);
}
雷同于P命名空间注入,
需要在头文件中加入约束文件xmlns:c="http://www.springframework.org/schema/c"
,且实体类中要存在有参构造器,
<!--c命名空间注入,通过构造器注入:construct-args-->
<bean id="userC" class="com.idiot.pojo.User" c:age="3" c:name="idiot"/>
AOP
(Aspect Oriented Programming
)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP
是 OOP
的延续,是软件开发中的一个热点,也是 Spring
框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP
可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
提供声明式事务;允许用户自定义切面
ASPECT
):横切关注点被模块化的特殊对象。即它是一个类。Advice
):切面必须要完成的工作。即它是类中的一个方法。Target
):被通知对象。Proxy
):向目标对象应用通知之后创建的对象。PointCut
):切面通知执行的“地点”的定义。JointPoint
):与切入点匹配的执行点。SpringAOP
中,通过 Advice
定义横切逻辑,Spring
中支持5种类型的 Advice
:
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.aopalliance.intercept.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
即 Aop
在不改变原有代码的情况下,去增加新的功能,
使用AOP织入,需要导入一个依赖包,
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
编写业务接口 UserService.java
,
package com.idiot.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void search();
}
再编写实现类 UserServiceImpl.java
,
package com.idiot.service;
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("查询用户");
}
}
然后开始 AOP
环节,我们编写两个通知类 , 前置通知 BeforeLog.java
, 后置通知 AfterLog.java
,
BeforeLog.java
package com.idiot.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
//method : 要执行的目标对象的方法
//args : 被调用的方法的参数
//target : 目标对象
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println( target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
AfterLog.java
package com.idiot.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
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);
}
}
最后编写配置文件进行 Spring
中的注册,并实现 aop
切入实现,注意导入约束, 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.idiot.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.idiot.log.BeforeLog"/>
<bean id="afterLog" class="com.idiot.log.AfterLog"/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.idiot.service.UserServiceImpl.*(..))"/>
<!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
expression
是一个表达式,其中 expression="execution()"
这是固定的,execution()
的括号里填写的参数是要执行的位置,
这是如何判断的呢?
大概是:修饰词 -> 返回值 -> 类名 -> 方法名 -> 参数值,
* com.idiot.service.UserServiceImpl.*(..)
,第一个*
表示类名前面可以是任意的修饰词和返回值,第二个*
则表示任意的方法名,..
表示任意几个参数,
测试 MyTest.java
,
import com.idiot.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意:动态代理代理的是接口
UserService userService = context.getBean("userService",UserService.class);
userService.search();
}
}
Spring
的 Aop
就是将公共的业务 (日志,安全等) 和领域业务结合起来,当执行领域业务时,将会把公共业务加进来,实现公共业务的重复利用 ,领域业务更纯粹,程序猿专注领域业务,其本质还是动态代理,
目标业务类不变依旧是 userServiceImpl.java
,
DiyPointcut.java
,package com.idiot.diy;
public class DiyPointcut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
applicationContext.xml
,<!--注册bean-->
<bean id="userService" class="com.idiot.service.UserServiceImpl"/>
<bean id="diy" class="com.idiot.diy.DiyPointcut"/>
<!--aop的配置-->
<aop:config>
<!--使用AOP的标签实现-->
<aop:aspect ref="diy">
<aop:pointcut id="diyPointcut" expression="execution(* com.idiot.service.UserService.*(..))"/>
<aop:before pointcut-ref="diyPointcut" method="before"/>
<aop:after pointcut-ref="diyPointcut" method="after"/>
</aop:aspect>
</aop:config>
MyTest.java
,import com.idiot.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//注意:动态代理代理的是接口
UserService userService = context.getBean("userService",UserService.class);
userService.search();
}
}
AnnotationPointcut.java
,package com.idiot.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.idiot.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.idiot.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.idiot.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
Spring
配置文件中注册 bean
,并增加支持注解的配置,<bean id="annotationPointcut" class="com.idiot.diy.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>
aop:aspectj-autoproxy
说明:
通过 aop
命名空间的 <aop:aspectj-autoproxy />
声明自动为 Spring
容器中那些配置 @aspect
切面的 bean
创建代理,织入切面,
当然,Spring
在内部依旧采用 AnnotationAwareAspectJAutoProxyCreator
进行自动代理的创建工作,但具体实现的细节已经被 <aop:aspectj-autoproxy />
隐藏起来了,
<aop:aspectj-autoproxy/>
有一个proxy-target-class
属性,默认为 false
,表示使用 jdk 动态代理织入增强,
当配为 <aop:aspectj-autoproxy poxy-target-class="true"/>
时,表示使用 CGLib 动态代理技术织入增强,
不过即使 proxy-target-class
设置为 false
,如果目标类没有声明接口,则 Spring
将自动使用CGLib动态代理。
先是导入依赖包,配置仓库,同时不要忘记配置 Maven
静态资源过滤, pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Spring</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<!-- aspectJ AOP 织入器 -->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!--配置Maven静态资源过滤问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
编写实体类 User.java
,
package com.idiot.pojo;
import lombok.Data;
@Data
public class User {
private int id; //id
private String name; //姓名
private String pwd; //密码
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
再编写接口 UserMapper.java
及其映射文件 UserMapper.xml
,
UserMapper.java
package com.idiot.mapper;
import com.idiot.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.idiot.mapper.UserMapper">
<select id="selectUser" resultType="User">
select * from user
</select>
</mapper>
紧接着编写核心配置文件 mybatis-config.xml
以及外部配置文件 db.properties
,
mybatis-config.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入外部配置文件-->
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="pwd" value="123456"/>
</properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.idiot.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.idiot.mapper.UserMapper"/>
</mappers>
</configuration>
db.properties
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3307/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
最后进行测试,编写测试类 MyTest.java
,这里没有像之前那样特意的去编写工具类了,然是直接在测试类中进行 SqlSession
的一系列操作,
import com.idiot.mapper.UserMapper;
import com.idiot.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyTest {
@Test
public void selectUser() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user: userList){
System.out.println(user);
}
sqlSession.close();
}
}
什么是 MyBatis-Spring
?
MyBatis-Spring
就是帮助你将 MyBatis
代码无缝地整合到 Spring
中。
MyBatis-Spring
需要以下版本:
MyBatis-Spring | MyBatis | Spring 框架 | Spring Batch | JDK |
---|---|---|---|---|
2.0 | 3.5+ | 5.0+ | 4.0+ | Java 8+ |
1.3 | 3.4+ | 3.2.2+ | 2.1+ | Java 6+ |
因此需要在 pom.xml
中加入以下代码:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
要和 Spring
一起使用 MyBatis
,需要在 Spring
应用上下文中定义至少两样东西:一个 SqlSessionFactory
和至少一个数据映射器类。
在 MyBatis-Spring
中,可使用 SqlSessionFactoryBean
来创建 SqlSessionFactory
。要配置这个工厂 bean
,只需要把下面代码放在 Spring
的 XML 配置文件中:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
注意:SqlSessionFactory
需要一个 DataSource
(数据源),且是唯一的必要属性。这可以是任意的 DataSource
,只需要和配置其它 Spring
数据库连接一样配置它就可以了。
在基础的 MyBatis
用法中,是通过 SqlSessionFactoryBuilder
来创建 SqlSessionFactory
的。而在 MyBatis-Spring
中,则使用 SqlSessionFactoryBean
来创建。
在 MyBatis
中,你可以使用 SqlSessionFactory
来创建 SqlSession
。一旦你获得一个 session
之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session
。
一个常用的属性是 configLocation
,它用来指定 MyBatis
的 XML 配置文件路径。它在需要修改 MyBatis
的基础配置非常有用。通常,基础配置指的是 < settings>
或 < typeAliases>
元素。
需要注意的是,这个配置文件并不需要是一个完整的 MyBatis
配置。确切地说,任何环境配置,数据源和 MyBatis
的事务管理器都会被忽略。SqlSessionFactoryBean
会创建它自有的 MyBatis
环境配置(Environment
),并按要求设置自定义环境的值。
SqlSessionTemplate
是 MyBatis-Spring
的核心。作为 SqlSession
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession
。
模板(Template
)可以参与到 Spring
的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate
来替换 MyBatis
默认的 DefaultSqlSession
实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用 SqlSessionFactory
作为构造方法的参数来创建 SqlSessionTemplate
对象。
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
现在这个bean
就可以直接注入到Mapper bean
中,
package com.idiot.mapper;
import com.idiot.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
//sqlSession不用我们创建,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
接下来注入 SqlSessionTemplate
:
<bean id="userMapper" class="com.idiot.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
Spring
的配置文件 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">
</beans>
mybaits
的数据源,<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3307/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
SqlSessionFactory
来关联 MyBatis
,<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/idiot/mapper/*.xml"/>
</bean>
sqlSessionTemplate
,关联 sqlSessionFactory
,<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
Mapper
接口的实现类 UserMapperImpl.java
,私有化 sqlSessionTemplate
,package com.idiot.mapper;
import com.idiot.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
//sqlSession不用我们自己创建了,Spring来管理
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
bean
实现,<bean id="userMapper" class="com.idiot.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
Mybatis
的配置文件,发现所有内容都可以被 Spring
整合,<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.idiot.pojo"/>
</typeAliases>
</configuration>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = (UserMapper) context.getBean("userMapper");
List<User> user = mapper.selectUser();
System.out.println(user);
}
mybatis-spring1.2.3
版以上的才有这个,
dao
继承 Support
类 , 直接利用 getSqlSession()
获得 , 然后直接注入 SqlSessionFactory
,比起方式一,不需要管理 SqlSessionTemplate
, 而且对事务的支持更加友好,可跟踪源码查看,
修改 UserMapperImpl.java
,
package com.idiot.mapper;
import com.idiot.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
public List<User> selectUser() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
return mapper.selectUser();
}
}
修改 applicationContext.xml
中 bean
的配置,
<bean id="userMapper" class="com.idiot.mapper.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
测试,
总结 : 整合到 Spring
以后可以完全不要 mybatis
的配置文件,除了这些方式可以实现整合之外,我们还可以使用注解来实现,
以上内容只是简略的有重点的描述了 Spring
框架,要真正掌握还是需要自己继续深入学习的,谨记面向百度编程!