Spring(二)

共两部分

参考

https://www.bilibili.com/video/BV1WE411d7Dv

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core

Bean作用域

he following table describes the supported scopes:

Scope Description
singleton (Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
prototype Scopes a single bean definition to any number of object instances.
request Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
session Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
application Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
websocket Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.

1、单例模式

单例模式

2、原型模式

原型模式

3、其余 request、session、application 在 web 开发使用

Bean自动装配

1
2
3
4
5
6
7
public class People {
private String name;
private Dog dog;
private Cat cat;

。。。
}

ByName自动装配

1
2
3
4
5
6
7
<bean id="dog" class="com.wnh.pojo.Dog"/>
<bean id="cat" class="com.wnh.pojo.Cat"/>

<!-- 属性与 bean 中已配置id一致 -->
<bean id="people" class="com.wnh.pojo.People" autowire="byName">
<property name="name" value="小杨"/>
</bean>

ByType自动装配

1
2
3
4
5
6
7
<bean id="dog2" class="com.wnh.pojo.Dog"/>
<bean id="cat" class="com.wnh.pojo.Cat"/>

<!-- 属性与 bean 中已配置类型一致 -->
<bean id="people" class="com.wnh.pojo.People" autowire="byType">
<property name="name" value="小杨"/>
</bean>

no/constructor

注解实现自动装配

JDK1.5支持注解,Spring2.5支持注解

1
2
3
4
5
6
7
8
9
10
11
12
<?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>

@Autowried

直接在属性前使用,也可在 set 方法上使用

使用 Autowried 就不用编写 set 方法了,前提是这个装配的属性在 IOC 容器中存在,且符合 ByName

1
@Nullable 表明字段可为空

Autowired 默认required为 true(非空,不接受空)

1
2
3
public @interface Autowired {
boolean required() default true;
}

设置 false 则可空

1
@Autowired(required = false)

@Primary

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-autowired-annotation-primary

@Qualifier

限定符

1
2
3
4
5
6
7
8
public class MovieRecommender {

@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;

// ...
}

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-autowired-annotation-qualifiers

@Resource注解

1
2
3
4
5
6
7
8
9
10
public class People {

private String name;
@Resource(name = "dog2")
private Dog dog;
@Autowired
private Cat cat;

。。。
}

@Resource 和@Autowried 区别:

  • 都用来自动装配,都用在属性字段上
  • @Autowried 通过 ByName 的方式实现
  • @Resource 默认通过 ByName 的方式实现,如果找不到名字,则通过 ByType 实现,如果都找不到报错

注解开发

必须保证 aop 的包导入

使用注解需要导入 context 约束,增加注解支持

1
2
3
4
5
6
7
8
9
10
11
12
<?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>

包扫描

1
2
<!-- 指定要扫描的包,这个包下的注解就会生效 -->
<context:component-scan base-package="com.wnh.pojo"/>

属性注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Component
public class User {
/**
* 等价于 <property name="name" value="老杨"/>
*/

public String name;

public String getName() {
return name;
}
@Value("老杨")
public void setName(String name) {
this.name = name;
}
}

@Component 衍生注解,根据 web 分层:

  • dao层注解:@Repository

  • service层注解:@Service

  • 控制器层注解:@Controller

作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Component
@Scope("prototype")
public class User {
/**
* 等价于 <property name="name" value="老杨"/>
*/

public String name;

public String getName() {
return name;
}
@Value("老杨")
public void setName(String name) {
this.name = name;
}
}

xml 与注解

  • xml 更万能,使用任何场合,维护方便
  • 注解维护复杂

Java 方式配置 Spring

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-java

AOP

代理模式

https://www.cnblogs.com/daniels/p/8242592.html

http://c.biancheng.net/view/1359.html

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式的主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
  • 代理对象可以扩展目标对象的功能;
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是:

  • 代理模式会造成系统设计中类的数量增加
  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  • 增加了系统的复杂度;

静态代理

静态代理总结:

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

动态代理

需要两个类:Proxy:代理,InvocationHandler:调用处理程序

接口

1
2
3
public interface BuyHouse {
void buyHouse();
}

实现类

1
2
3
4
5
6
public class BuyHouseImpl implements BuyHouse{
@Override
public void buyHouse() {
System.out.println("buyHouse");
}
}

动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class DynamicProxyHandler implements InvocationHandler {

private Object object;

public DynamicProxyHandler(final Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("before");
Object result=method.invoke(object,args);
System.out.println("after");
return result;
}
}

测试

1
2
3
4
5
6
7
public class MyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHouse();
}
}

AOP实现

导入依赖包

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>

方式一

原生 Spring API接口

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
<?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
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.wnh.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.wnh.log.BeforeLog"/>
<bean id="afterLog" class="com.wnh.log.AfterLog"/>

<!-- 方式一:使用原生 Spring API 接口 -->
<!-- 配置 aop 需要导入 aop 的约束 -->
<aop:config>
<!-- 切入点 execution:表达式 execution(要执行的位置 修饰词 返回值 类名 方法名 参数)-->
<aop:pointcut id="pointcut" expression="execution(* com.wnh.service.UserServiceImpl.*(..))"/>

<!-- 执行环绕 -->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>

<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>

UserService 接口

1
2
3
4
5
6
public interface UserService {
void add();
void delete();
void update();
void select();
}

UserServiceImpl 实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("add");
}

@Override
public void delete() {
System.out.println("delete");
}

@Override
public void update() {
System.out.println("update");
}

@Override
public void select() {
System.out.println("select");
}
}

BeforeLog

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BeforeLog implements MethodBeforeAdvice {
/**
* @param method 要执行的目标对象的方法
* @param args 参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}

}

AfterLog

1
2
3
4
5
6
7
8
9
10
11
12
13
public class AfterLog implements AfterReturningAdvice {
/**
* @param returnValue 返回值
* @param method
* @param args
* @param target
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+ method.getName()+"方法,返回值为:"+returnValue);
}
}

测试

1
2
3
4
5
6
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}

方式二

自定义类

1
2
3
4
5
6
7
8
public class DiyPointCut {
public void before(){
System.out.println("before");
}
public void after(){
System.out.println("after");
}
}

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 方式二:自定义类 -->
<bean id="diy" class="com.wnh.diy.DiyPointCut"/>

<aop:config>
<!-- 自定义切面,ref 要引用的类 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="point" expression="execution(* com.wnh.service.UserServiceImpl.*(..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="point"/>
<aop:before method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>

测试同上

方式三

注解

1
2
3
4
<!-- 方式三:注解 -->
<bean id="annotationPoint" class="com.wnh.diy.AnnotationPoint"/>
<!-- 开启注解支持 -->
<aop:aspectj-autoproxy/>

类注解

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
@Aspect // 标注为切面
public class AnnotationPoint {
@Before("execution(* com.wnh.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("before");
}
@After("execution(* com.wnh.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("after");
}
// 可以给定一个参数,代表我们要获取的切入点
@Around("execution(* com.wnh.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("be-around");

Signature signature = jp.getSignature();
System.out.println("signature:"+signature);

// 执行方法
Object proceed = jp.proceed();

System.out.println(proceed);

System.out.println("af-around");

}
}

测试同上

整合 Mybatis

https://mybatis.org/spring/zh/index.html

Mybatis-Spring

在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要——因为本手册中不会提供二者的基本内容,安装和配置教程。

MyBatis-Spring 需要以下版本:

MyBatis-Spring MyBatis Spring Framework Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

安装

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>

mybatis-config.xml 只留下 typeAliases 和 setting 即可

1
2
3
4
5
6
7
8
9
10
11
<?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>

<typeAliases>
<package name="com.wnh.pojo"/>
</typeAliases>

</configuration>

SqlSessionTemplate

1、编写数据源

2、sqlSessionFactory

3、sqlSessionTemplate

4、接口实现类

UserMapperImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class UserMapperImpl implements UserMapper {
/**
* 在原来所有操作都使用 sqlSession 来操作,现在使用 SqlSessionTemplate
*/
private SqlSessionTemplate sqlSession;

public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}

@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}

5、将实现类注入 Spring

spring-dao.xml

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
<?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">

<!-- DataSource:使用 spring 的数据源替换 mybatis 的配置 cp30 dbcp druid -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>

<!-- 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/wnh/mapper/*.xml"/>
</bean>

<!-- SqlSessionTemplate 就是我们使用的 SqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

</beans>

applicationConext.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<import resource="spring-dao.xml"/>

<bean id="userMapper" class="com.wnh.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>

6、测试

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
@Test
public void test1() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sessionFactory.openSession(true);

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void test2() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper mapper = context.getBean("userMapper",UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}
@Test
public void test3() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = context.getBean("userMapper",UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}

SqlSessionDaoSupport

UserMapperImpl2 继承 SqlSessionDaoSupport

1
2
3
4
5
6
7
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {

return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}

注入

1
2
3
<bean id="userMapper2" class="com.wnh.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

测试

1
2
3
4
5
6
7
8
9
@Test
public void test4() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper mapper = context.getBean("userMapper2",UserMapper.class);
List<User> users = mapper.selectUser();
for (User user : users) {
System.out.println(user);
}
}

事务(transaction)

标准配置

要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:

1
2
3
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource" />
</bean>

共两部分