Spring是一个众多的企业级应用技术还能整合开源世界众多著名的轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
Spring
1.Spring简介
Spring是分层的Java SE/EE应用fll-stack轻量级开源框架,以IoC ( Inverse Of Control :反转控制)和AOP ( Aspect Oriented Programming :面向切面编程)为内核。
提供了展现层SpringMVC和持久层Spring JDBCTemplate以及业务层事务管理等众多的企业级应用技术还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE企业应用开源框架。
总结一句话: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
2.开发步骤
- 图示:

- 详解文字表示
- 导入Spring开发的基本包坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
- 编写Dao接口实现类
//创建接口
public interface UserDao {
public void save();
}
//实现类
public class UserDaoImpl implements UserDao {
public void save(){
System.out.println("save is running...");
}
}
- 创建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
https://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean id="userDao" class="com.qd.dao.UserDaoImpl"/>
</beans>
- 测试类
public static void main(String[] args) {
//获取spring的上下文对象
//在配置文件加载的时候,容器中的对象就已经初始化了!:Bean
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao UserDao = (UserDao) context.getBean("userDao");
UserDao.save();
}
3.依赖注入
属性
- property设置属性: value:直接赋值 ref:要引用spring容器创建好的对象
- 有参 直接通过参数名匹配 推荐使用 :<constructor-arg name="str"value="qd123"/>
- spring配置 别名 :< alias name="hello" alias="bieming"/>
- import 将多个配置文件导入,合并为一个 :< import resource="beans.xml"/>
- bean属性(见图)
说明 | |
---|---|
id | bean的唯一标识符。也就是相当于对象名 |
class | bean对象所对应的权限定名:包名+类名 |
name | 取别名 可以取多个别名qd1,qd2 逗号或者空格分割或者分号 |
scope | singleton 单例模式 默认为单例模式 加载配置文件时候创建 prototype:原型模式 getBean 时候创建 |
autoWire | 自动装配 byName:会自动在容器上下文查找和自己对象set方法后面的对应的beanid bytype无需id也能跑起来 |
注入方法
- 构造输入与set注入 (下面为set注入示例)
<!-- set方式注入 重点!!!-->
<!-- 依赖注入:set注入-->
<!-- 依赖:bean对象的创建依赖容器-->
<!-- 注入:bean对象中的所有属性,由容器注入-->
<bean id="address" class="com.qd.pojo.Address"/>
<bean id="student" class="com.qd.pojo.Student">
<!-- 普通值注入 value-->
<property name="name" value="前度"/>
<!-- bean注入 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="sfz" value="123456"/>
<entry key="yhk" value="654321"/>
</map>
</property>
<!-- wife null-->
<property name="wife">
<null/>
</property>
<!-- Properties-->
<property name="info">
<props>
<prop key="学号">201952050201</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
- p命名空间注入 可以直接注入属性的值 : < bean id="student" class= " " p:name=" "/>
4.Spring相应API
- ApplicationContext的实现类
ClassPathXmlApplicationContext | 它是从类的根路径下加载配置文件推荐使用这种 |
---|---|
FileSystemXmlApplicationContext | 它是从磁盘路径.上加载配置文件,配置文件可以在磁盘的任意位置 |
AnnotationConfigApplicationContext | 当使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解 |
getBean(String name) | 通过id或name去查找获取bean,不能重名 |
5.配置数据源
普通配置数据源
- 导入数据源的坐标和数据库驱动坐标
- 创建数据源对象
- 设置数据源的基本连接数据
- 使用数据源获取连接资源和归还连接资源
- 导包
Spring基础 - 测试c3p0
Spring基础 - 测试 druid
Spring基础 - 数据库地址不唯一 在resource中新建file
内容为jdbc.properties
Spring基础 - 读取配置文件
用Spring配置数据源
- 导入spring坐标
- 在resource下创建applicationContext.xml
- 测试spring产生数据源对象 , 注意set方法的值
- spring加载properties文件 引入context
<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
">
- 加载外部文件
- 知识要点
6.注解开发
原始注解
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xm配置文件可以简化配置,提高开发效率。
原始注解主要代替的配置
注解 | |
---|---|
@Component | 使用在类上用于实例化Bean |
@Controller | 使用在web层类上用于实例化Bean |
@Service | 使用在service层类上用于实例化Bean |
@Repository | 使用在dao层类上用于实例化Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 结合@Autowired-起使用用于根据名称进行依赖注入 |
@Resource | 相当于@Autowired + @Qualifier,按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注Bean的作用范围 |
@PostConstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法是Bean的销毁方法 |
- 对比 xml与注解的使用不同
- xml
注意开启注解的支持 注解无需set方法
由于<context:component-scan base-package=”xx.xx”/> 也包含了自动注入上述Bean的功能,
所以< context:annotation-config /> 可以省略。如果两者都进行了配置,则只有前者有效。
< context:annotation-config > 是用于激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过package sanning的方式)上面的注解。
< context:component-scan >除了具有< context:annotation-config >的功能之外,
< context:component-scan >还可以在指定的package下扫描以及注册javabean 。
注解代码改进
由于前面我们不能够立马知道这个类在哪一层 所以更清楚的注解(看表2-4)
改演示未使用web环境 所以 @Controller 暂未演示
注解无需set方法 图中均带有注意注意~~~
//< property name="userDao" ref="userDao" >< /property >
@Autowired //根据数据类型从Spring容器中匹配的
@Qualifier("userDao") //按照id名称从容器匹配 此处要结合Autowired一起用 按照类型注入 此行可以省略
@Resource(name = "userDao") //@Resource相当于@Autowired + @Qualifier
更多属性注解
- Value
@Value("${jdbc.driver}")
private String driver;
- Scope
@Scope("singleton")
//@Scope("prototype")
- PostConstruct ---- PreDestroy ----- 非重点
@PostConstruct
public void init() {
System.out.println("对象的初始化方法");
}
@PreDestroy()
public void destory() {
System.out.println("对象的销毁方法");
} //要手动关闭
Spring新注解
使用上面的注解还不能全部替代xm配置文件,还需要使用注解替代的配置如下:
非自定义的Bean的配置: < bean >
加载properties文件的配置: < context:property-placeholder >
组件扫描的配置: < context:component-scan >
引入其他文件: < import >
- 新方法
详解 | |
---|---|
@Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定Spring在初始化容器时要扫描的包。 作用和在Spring的xml配置文件中的。 <context:component-scan base package="com.itheima"/>一样 |
@Bean | 使用在方法上,标注将该方法的返回值存储到Spring容器中 |
@PropertySource | 用于加载properties文件中的配置 |
@Import | 用于导入其他配置类 |
- 专门建一个核心配置类 这里叫 SpringConfiguration 对比一下
- 标志 :该类是spring的核心配置类
@Configuration
- 引入其他资源
//<import resource="xxx"/>
@Import({xxx.class,xxxx.class}) //参数为数组 可以传多个class文件
- 开启注解的支持
//<context:component-scan base-package="com.qd"/>
@ComponentScan("com.qd")
- 加载配置文件
//<context:property-placeholder location="classpath: jdbc.properties"/>
@PropertySource("classpath: jdbc.properties")
演示代码
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
public DataSource getDataSource() {
@Bean("dataSource") //Spring会将当前方法的返回值以指定名称存储到Spring容器中
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
测试代码
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = context.getBean(UserService.class);
userService.save();
7.集成Junit
原始Junit测试Spring的问题
在测试类中 每个方法都有以下两行代码,这两行代码的作用是获取容器,如果不写的话,直接会提示 空指针异常。所以又不能轻易删掉。
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean(UserService.class);
解决方法
让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
将需要进行测试Bean直接在测试类中进行注入
- 导入spring集成Junit的坐标
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--Spring-Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- 使用@Runwith注解替换原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)
- 使用@ContextConfiguration指定配置文件或配置类
@ContextConfiguration("classpath:applicationContext.xml"); //xml
@ContextConfiguration(classes = {SpringConfiguration.class}) //注解
classes的内容为要编译的字节码文件
- 使用@Autowi red注入需要测试的对象
@Autowired
private userService userService;
@Autowired
private DataSource dataSource;
- 创建测试方法进行测试
@test
public void test(){
userService.save();
System.out.println(dataSource.getConnection());
}
- 图片
8.集成Web环境
- ApplicationContext应用上下文获取方式
应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件)方式获取的,但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件),这样的弊端是配置文件加载多次,应用上下文对象创建多次。
-
在Web项目中,可以使用ServletContextListener监听Web应用的启动, 我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext, 在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。
- 具体实现
-
代码优化
优化一 配置类
优化二
5.以上代码只做了解 在Spring中以对它做封装
上面的分析不用手动实现,Spring提供了一 个监听器ContextLoaderListener就是对 上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中, 是供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
所以我们需要做的只有两件事:
①在web.xmI中配置ContextLoaderListener监听器 (导入spring-web坐标)
②使用WebApplicationContextUtils获得应用 上下文对象ApplicationContext
<!-- Spring-Web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- web.xml加载
Spring基础 - 使用 获取应用上下文
9.代理模式
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】
代理模式的分类:
- 静态代理
- 动态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实的角色,代理真实角色后,我们一般会做附属操作
- 客户:访问代理的人!
代码步骤:
- 接口
package com.qd.demo01;
//租房
public interface Rent {
public void rent();
}
- 真实角色
package com.qd.demo01;
//房东
public class Host implements Rent {
public void rent() {
System.out.println("房东要出租房子");
}
}
- 代理角色
package com.qd.demo01;
//代理
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse() {
System.out.println("中介带你看房子");
}
//收费
public void fare() {
System.out.println("中介收中介费");
}
}
- 客户端访问代理角色
package com.qd.demo01;
//租客
public class Client {
public static void main(String[] args) {
//房东要出租房子
Host host = new Host();
//代理 会有一些附属操作
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介即可
proxy.rent();
}
}
静态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共的业务也交了给了代理角色!实现了业务的分工!
- 公共业务发展扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色;代码量翻倍~~ 开发效率变低~~
加深理解
示例代码:
- 创建一个
的接口 内有增删改查的功能UserService.java
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
- 接口的实现类
UserServiceImpl.java
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 query() {
System.out.println("查询了一用户");
}
}
-
此时想法是每调用一次方法都想加个日志输出 需要用到代理
我们编写一个
具体内容为:UserServiceProxy.java
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy() {
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.add();
}
public void update() {
log("update");
userService.add();
}
public void query() {
log("query");
userService.add();
}
public void setUserService(UserService userService) {
this.userService = userService;
}
//日志方法
public void log(String msg) {
System.out.println("[debug]使用了" + msg + "方法");
}
}
- 编写一个测试类
Client.java
public class Client {
public static void main(String[] args) {
// UserServiceImpl userService = new UserServiceImpl();
// userService.add();
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.add();
}
}
- 动态代理与静态代理角色一样!
- 动态代理的代理类是动态生成的,不是写好的!
- 动态代理分为两大类:基于接口 ; 基于类
- 基于接口-----JDK动态代理
- 基于类---- cglib
- Java字节码实现: Javaassist
需要了解两个类 :
Proxy
:代理 ,
InvocationHandler
:调用处理程序
举个栗子:
- 通用代理类
//我们用这个类自动生成代理
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object target;
public void setTarget(Object target) {
this.target = 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 {
// log(method.getName());
Object result= method.invoke(target,args);
return result;
}
// //日志方法
// public void log(String msg) {
// System.out.println("[debug]使用了" + msg + "方法");
// }
}
- main方法
public static void main(String[] args) {
//真实角色
UserService userService = new UserServiceImpl();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setTarget(userService); //设置要代理的对象
Object proxy = pih.getProxy(); //动态生成代理类 此处要强转
}
- 动态代理的好处
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务!
10.AOP
什么是AOP?
AOP (Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的-种技术。AOP是O0P的延续,是软件开发中的一个热点,也是Spring框架中的一 -个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
使用Spring实现Aop
【重点】使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.assertj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
-
【方式1】使用Spring的API接口
- 日志类
配置xml文件
测试
-
【方式2】自定义来实现AOP,主要是切面定义
自定义一个类
配置xml文件
-
【方式3】使用注解来实现AOP
结果
11.更多
1.在maven中新建Spring项目
- 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">
<modelVersion>4.0.0</modelVersion>
<!-- 父工程-->
<groupId>org.example</groupId>
<artifactId>Maven</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring</module>
</modules>
<!-- 导入依赖-->
<dependencies>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.8</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- Spring-Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
</dependency>
</dependencies>
<!--在build中配置resources,来防止我们的资源导出问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!-- <filtering>true</filtering>-->
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!-- <filtering>true</filtering>-->
</resource>
</resources>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- Spring的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>Maven</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring</artifactId>
</project>
结束啦~~