文章目录
Spring一、概述二、体系结构三、Spring IOC3.1 解决程序的耦合3.2 ApplicationContext3.3 创建bean的三种方式3.4 bean的作用范围3.5 bean的生命周期3.5.1 单例对象3.5.2 多例对象
四、Spring依赖注入4.1 概述4.2 依赖注入
五、基于注解的IOC5.1 用于创建对象5.2 用于注入数据5.3 用于作用范围5.4 生命周期
六、spring新注解七、AOP7.1 动态代理7.1.1 基于接口7.1.2 基于子类
7.2 概述7.3 AOP相关术语7.4 Spring基于XML的AOP配置7.5 Spring基于注解的AOP配置
八、Spring中的JDBCTemplate8.1 基本配置8.2 DAO中的使用
Spring
一、概述
Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IoC(Inverse Of Control;反转控制)和AOP(Aspect Oriented Programming;面向切面编程)为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架
二、体系结构
核心容器:核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression等模块组成
spring-core:提供了框架的基本组成部分,包括IoC和依赖注入功能spring-beans:提供BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要context:建立在有core和beans模块的基础上建立起来的,它以一种类似于JNDI注册的方式反问对象。spring-expression:提供了强大的表达式语言,用于在运行时查询和操作对象图
数据访问与集成:JDBC、ORM、OXM、JMS和事务处理模块
JDBC:提供了JDBC抽象层,它消除了冗长的JCBC编码和对数据库供应商特定错误代码的解析ORM:提供了对流行的对象关系映射API的继承,包括JPA、JDO和Hibernate等OXM:提供了对OXM实现的支持JMS:包含生产和消费的功能。事务:实现特殊接口类以及所有的POJO支持编程方式和声明式事务
Web:Web、Web-MVC、Web-Socket和Web-Portlet
Web:提供Web基本功能和面向Web的应用上下文,比如部分文件上传功能、使用Servlet监听器初始化IoC容器等Web-MVC:为Web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring MVC框架可以使领域模型代码和web表单完全地分离Web-Socket:为WebSocket-based提供了支持,而且在web应用程序中提供了客户端和服务器之间的通信方式Web-Portlet:提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能
其他
AOP:提供了面向对象的编程实现,允许你定义定法拦截器和切入点对代码进行干净地解耦,从而实现功能代码彻底解耦出来Aspects:提供了AspectJ的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架Instrumentation:一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现Messaging:为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息
三、Spring IOC
3.1 解决程序的耦合
public interface IAccountDao {
void saveAccount();
}
public class AccountDao implements IAccountDao {
@Override
public void saveAccount() {
System
.out
.println("AccountDao");
}
}
public interface IAccountService {
void saveAccount();
}
public class AccountService implements IAccountService {
@Override
public void saveAccount() {
System
.out
.println("AccountService");
}
}
public class Client {
public static void main(String
[] args
) {
ApplicationContext ac
=new ClassPathXmlApplicationContext("bean.xml");
IAccountService as
= (IAccountService
)ac
.getBean("accountService");
IAccountDao adao
= ac
.getBean("accountDao", IAccountDao
.class);
System
.out
.println(as
);
System
.out
.println(adao
);
}
}
<?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="accountService" class="ysw.service.impl.AccountService"></bean>
<bean id="accountDao" class="ysw.dao.impl.AccountDao"></bean>
</beans>
3.2 ApplicationContext
三个常用实现类
ApplicationContext ac
= new ClassPathXmlApplicationContext();
ApplicationContext ac
= new FileSystemXmlApplicationContext();
ApplicationContext ac
= new AnnotationConfigApplicationContext();
核心容器的两个接口
ApplicationContext:构建核心容器时,创建对象采取的策略是采用立即加载的方式,只要读完配置文件马上就会创建出对象BeanFactory:构建核心容器时,创建对象采用延迟加载的方式,什么时候根据id获取对象,则什么时候才真正创建对象
3.3 创建bean的三种方式
使用默认构造函数,配id和class属性后,没有其他属性和标签使用普通工厂中的方法创建对象,并创建spring容器
<bean id="……" factory-bean="……" factory-method="……"></bean>
使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
3.4 bean的作用范围
使用标签的scope属性指定bean的作用范围
singleton:单例(默认值)prototype:多例request:作用于web应用的请求范围session:作用于web应用的会话范围global-session:作用于集群环境的会话范围
3.5 bean的生命周期
3.5.1 单例对象
出生:当容器创建时对象出生活着:只要容器还在,对象一直活着死亡:容器销毁,对象消亡总结:单例对象的声明周期和容器相同
3.5.2 多例对象
出生:当使用对象时spring框架会为我们创建对象活着:对象在使用过程中一直活着死亡:当对象长时间不用,会由垃圾回收器进行回收
四、Spring依赖注入
4.1 概述
依赖注入:Dependency InjectionIOC的作用:降低程序间的耦合依赖的管理都交给spring管理当前类需要其他类的对象,则由spring为我们提供,我们只需在配置文件中说明
4.2 依赖注入
依赖注入的数据:
基本数据类型和String其他bean类型(在配置文件中或者注解配置过的bean)复杂类型/集合类型 注入方式:
使用构造函数提供使用set方法提供使用注解提供
构造函数注入
<bean id="……" class="……">
<constructor-arg name="……" value="……"></constructor-arg>
<constructor-arg name="……" ref="……"></constructor-arg>
</bean>
使用标签constructor-org标签出现的位置在bean标签内标签的属性:type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型;index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值,索引位置从0开始;name:用于指定给构造函数中指定名称的参数赋值;value:用于提供基本数据类型和String类型的数据;ref:用于指定其他的bean类型数据。指的是spring的Ioc核心容器中出现过的bean对象
set方法注入
<bean id="……" class="……">
<property name="……" value="……"></property>
<property name="……" ref="……"></property>
</bean>
set方法注入,涉及的标签为property出现的位置 在bean标签内部标签的属性:name:用于指定注入时所调用的set方法名称;value:用于提供基本数据类型和String类型数据;ref:用于指定其他的bean类型数据。它指的是在spring的Ioc和兴容器中出现过的对象
复杂类型注入(集合类型注入)
用于给List结构集合注入的标签:list array set用于Map结构集合注入的标签:map props
<bean id="……" class="……">
<property name="……">
<list>
<value>……
</value>
<value>……
</value>
<value>……
</value>
</list>
</property>
<property name="……">
<array>
<value>……
</value>
<value>……
</value>
<value>……
</value>
</array>
</property>
<property name="……">
<map>
<entry key="……" value=""></entry>
<entry key="……">
<value>……
</value>
</entry>
</map>
</property>
<property name="……">
<props>
<prop key="……">……
</prop>
</props>
</property>
</bean>
五、基于注解的IOC
5.1 用于创建对象
<?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:component-scan base-package="包名"></context:component-scan>
</beans>
@Component("accountService")
public class AccountService implements IAccountService {
@Override
public void saveAccount() {
System
.out
.println("AccountService");
}
}
Component:用于把当前类对象存入spring容器中
属性value:用于指定bean的id。当不写时,他默认为当前类名且首字母改小写 Controller:一般用在表现层Service:一般用在业务层Repository:一般用在持久层他们三个注解的作用和属性与Component一模一样他们三个是spring框架为我们提供明确的三层使用的注解
5.2 用于注入数据
Autowired:
自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功可以出现在变量上或方法上使用注解注入时,set方法就不是必须的 Qualifier:
在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用属性:value:用于指定注入bean的id Resource:
直接按照bean的id注入。它可以独立使用属性:name:用于指定bean的id Value:
用于注入基本类型和String类型的数据属性value:用于指定数据的值。它可以使用spring中SpEL(spring的el表达式)SpEL写法:${表达式}
5.3 用于作用范围
Scope:用于指定bean的作用范围属性value:指定范围的取值:singleton、prototype
5.4 生命周期
他们的作用和在bean标签中使用init-method和destroy-method作用一样PreDestroy:用于指定销毁方法PostConstruct:用于指定初始化方法
六、spring新注解
Configuration
指定当前类为配置类 ComponentScan
用于通过注解指定spring在创建容器时要扫描的包属性value:与basePackages作用一样,用于指定创建容器时需要扫描的包 Bean
用于当前方法的返回值作为bean对象存入spring的IoC容器中属性name:用于指定bean的id,当不写时,默认值为当前方法的名称
@Configuration
@ComponentScan("ysw.service")
class SpringConfiguration {
@Bean("runner")
public QueryRunner
createQueryRunner(DataSource dataSource
) {
return new QueryRunner(dataSource
);
}
@Bean("dataSource")
public DataSource
createDataSource() {
ComboPooledDataSource ds
= new ComboPooledDataSource();
try {
ds
.setDriverClass("com.mysql.jdbc.Driver");
ds
.setJdbcUrl("jdbc:mysql://localhost:3306/……");
ds
.setUser("root");
ds
.setPassword("……");
return ds
;
} catch (PropertyVetoException e
) {
e
.printStackTrace();
}
return null
;
}
}
import
用于导入其他的配置类属性value:用于指定其他配置类的字节码,有Import注解的类都是父配置类,而导入的类都是子配置类
PropertySource
用于指定properties文件的位置属性value:指定文件的名称和路径;关键字classpath,表示类路径
Spring整合Junit单元测试
导入spring整合Junit的jar使用Junit提供的一个注解把原有的main方法替换,替换成spring提供的:@Runwith告知spring的运行器,spring和IoC创建基于xml还是注解的,并说名位置:@ContextConfigurationlocation:指定xml文件的位置,加上classpath关键字,表示在类路径下classes:指定注解类所在的位置
七、AOP
7.1 动态代理
7.1.1 基于接口
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法增强
分类:
基于接口的动态代理基于子类的动态代理
基于接口的动态代理:
涉及的类:Proxy提供者:JDK官方
创建代理:
使用Proxy类中的newProxyInstance方法
创建代理对象的要求:
被代理类最少实现一个接口,如果没有则不能使用
newProxyInstance的参数
ClassLoader:用于加载代理对象字节码。和被代理对象使用相同的类加载器Class[]:用于让代理对象和被代理对象有相同的方法InvocationHandler:用于提供增强的代码。一般写一个该接口的实现类,通常情况下是匿名内部类
代码演示
import java
.lang
.reflect
.InvocationHandler
;
import java
.lang
.reflect
.Method
;
import java
.lang
.reflect
.Proxy
;
public class Client {
public static void main(String
[] args
) {
final Producer p
= new Producer();
IProducer proxyProducer
= (IProducer
) Proxy
.newProxyInstance(p
.getClass().getClassLoader(), p
.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object
invoke(Object proxy
, Method method
, Object
[] args
) throws Throwable
{
Object returnValue
= null
;
float money
= (float) args
[0];
if ("saleProduct".equals(method
.getName())) {
returnValue
= method
.invoke(p
, money
* 0.8f);
}
return returnValue
;
}
});
proxyProducer
.saleProduct(1000f);
}
}
7.1.2 基于子类
基于接口的动态代理:
涉及的类:Enhancer提供者:第三方cglib库 创建代理:
使用Enhancer类中的create方法 创建代理对象的要求:
被代理类不能是最终类 create的参数
ClassLoader:用于加载代理对象字节码。和被代理对象使用相同的类加载器Class字节码:用于指定被代理对象的字节码Callback:用于提供增强的代码。一般写一个该接口的实现类,通常情况下是匿名内部类 代码演示
import net
.sf
.cglib
.proxy
.Enhancer
;
import net
.sf
.cglib
.proxy
.MethodInterceptor
;
import net
.sf
.cglib
.proxy
.MethodProxy
;
import java
.lang
.reflect
.Method
;
public class Client {
public static void main(String
[] args
) {
final Producer p
= new Producer();
Producer cglibProducer
= (Producer
) Enhancer
.create(p
.getClass(), new MethodInterceptor() {
@Override
public Object
intercept(Object proxy
, Method method
, Object
[] args
, MethodProxy methodProxy
) throws Throwable
{
Object returnValue
= null
;
Float money
= (Float
) args
[0];
if ("saleProduct".equals(method
.getName())) {
returnValue
= method
.invoke(p
, money
* 0.8f);
}
return returnValue
;
}
});
cglibProducer
.saleProduct(1200f);
}
}
7.2 概述
AOP为Aspect Oriented Programming的缩写,意为面向切面编程,通过预编译方式运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架的一个重要内容。
作用:
在程序运行期间,不修改源码对已有方法进行增强 优势:
减少重复代码提高开发效率维护方便
7.3 AOP相关术语
Joinpoint(连接点):
指那些被拦截的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点 Pointcut(切入点):
指我们对哪些Joinpoint进行拦截的定义 Advice(通知/增强):
指拦截到Joinpoint之后所要做的事情就通知通知类型:前置通知,后置通知,异常通知,最终通知,环绕通知 Introduction(引介):
引介是一种特殊的通知在不修改类型代码的前提下,Introduction可以在运行期为类动态的添加一些方法或Field Target(目标对象):
代理的目标对象 Weaving(织入):
把增强应用到目标对象来创建新的代理对象的过程Spring采用动态代理织入,而AspectJ采用编译器织入和类装在织入 Proxy(代理):
一个类被AOP织入增强后,就产生一个结果代理类 Aspect(切面):
是切入点和通知(引介)的结合
7.4 Spring基于XML的AOP配置
<dependency>
<groupId>org.aspectj
</groupId>
<artifactId>aspectjweaver
</artifactId>
<version>1.9.5
</version>
</dependency>
将通知Bean交给spring管理使用aop:config标签表明开始AOP的配置使用aop:aspect标签表明配置切面
id属性:给切面提供一个唯一标识ref属性:指定通知类bean的Id 在aop:aspect标签内部使用对应标签来配置通知的类型
下面示例是前置通知aop:before:表示配置前置通知;method属性:指定Logger类中哪个方法是前置通知pointcut属性:用于指定切入点表达式,该表达式指的是对业务层中哪些方法增强
<?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="accountService" class="ysw.service.impl.AccountServiceImpl"></bean>
<bean id="logger" class="ysw.utils.Logger"></bean>
<aop:config>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="printLog" pointcut="
execution(public void ysw.service.impl.AccountServiceImpl.saveAccount())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
全通配写法:* * . . * . *( . . )
7.5 Spring基于注解的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"
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/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">
<context:component-scan base-package="ysw.service"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
import org
.aspectj
.lang
.annotation
.*
;
import org
.springframework
.stereotype
.Component
;
@Component("logger")
@Aspect
public class Logger {
@Pointcut("execution(* ysw.service.impl.*.*(..))")
private void pt1(){}
@Before("pt1()")
public void beforePrintLog(){
System
.out
.println("前置通知!!!");
}
@AfterReturning("pt1()")
public void afterReturningPrintLog(){
System
.out
.println("后置通知!!!");
}
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System
.out
.println("异常通知!!!");
}
@After("pt1()")
public void afterPrintLog(){
System
.out
.println("最终通知!!!");
}
public void aroundPrintLog(){
System
.out
.println("环绕通知!!!");
}
}
八、Spring中的JDBCTemplate
8.1 基本配置
依赖
<dependencies>
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-context
</artifactId>
<version>5.2.5.RELEASE
</version>
</dependency>
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-tx
</artifactId>
<version>5.2.5.RELEASE
</version>
</dependency>
<dependency>
<groupId>org.springframework
</groupId>
<artifactId>spring-jdbc
</artifactId>
<version>5.2.5.RELEASE
</version>
</dependency>
<dependency>
<groupId>mysql
</groupId>
<artifactId>mysql-connector-java
</artifactId>
<version>5.1.6
</version>
</dependency>
</dependencies>
代码演示
public class JdbcTemplateDemo1 {
public static void main(String
[] args
) {
ClassPathXmlApplicationContext ac
= new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jt
= ac
.getBean("jdbcTemplate", JdbcTemplate
.class);
List
<Account> query
= jt
.query("select * from account where money>?",
new BeanPropertyRowMapper<Account>(Account
.class), 500);
for (Account account
: query
) {
System
.out
.println(account
);
}
Integer count
= jt
.queryForObject("select count(*) from account where money>?", Integer
.class, 500);
System
.out
.println(count
);
}
}
class AccountRowMapper implements RowMapper<Account> {
@Override
public Account
mapRow(ResultSet resultSet
, int i
) throws SQLException
{
Account a
= new Account();
a
.setId(resultSet
.getInt("id"));
a
.setName(resultSet
.getString("name"));
a
.setMoney(resultSet
.getDouble("money"));
return a
;
}
}
8.2 DAO中的使用
public class AccountDaoImpl implements IAccountDao {
private JdbcTemplate jdbcTemplate
;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate
) {
this.jdbcTemplate
= jdbcTemplate
;
}
@Override
public Account
findAccountById(Integer accountId
) {
List
<Account> query
= jdbcTemplate
.query("select * from account where id=?",
new BeanPropertyRowMapper<Account>(Account
.class), accountId
);
return query
.isEmpty() ? null
: query
.get(0);
}
@Override
public Account
findAccountByName(String accountName
) {
List
<Account> test
= jdbcTemplate
.query("select * from account where name=?", new BeanPropertyRowMapper<>(Account
.class), "test");
if (test
.isEmpty()) {
return null
;
}
if (test
.size() > 1) {
throw new RuntimeException("结果集不唯一!!!");
}
return test
.get(0);
}
@Override
public void updateAccount(Account account
) {
jdbcTemplate
.update("update account set name=? ,money=? where id=?",
account
.getName(), account
.getMoney(), account
.getId()
);
}
}