目录
1.3.http://localhost:1111/spring-basic.html - spring的版本Spring的版本
2.2.2.控制反转-Inversion Of Control
4.1.spring中的bean元素相关配置Spring中的bean元素相关配置
5.1.3.属性注入的注解@Value、@Autowired
spring (由Rod Johnson创建的一个开源框架)
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003年兴起的-一个轻量级的Java开发框架,由Rod Johnson创建。简单来说,Spring是一个分。层的JavaSE/EE full-stack(- 站式)轻量级开源框架。
Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为J2EE应用程序开发提供集成的框架。Spring使用基本的 JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 Spring的 核心是控制反转(IoC)和面向切面(AOP) 。简单来说, Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。
Expert One-to-One J2EE Design and Development : J2EE 的设计和开发:(2002.EJB)
Expert One-to-One J2EE Development without EJB : J2EE 不使用EJB的开发
1、方便解耦,简化开发
Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
2、AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
3、声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
4、方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序
5、方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
6、降低JavaEE API的使用难度
Spring对JavaEE开发中非常难用的一些API(如:JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
Spring3.X 和 Spring4.X 和 Spring5.X

在开发中可能会写很多的类,而有些类之间不可避免的产生依赖关系,这种依赖关系称之为耦合。
有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。
- public class CustomerServiceImpl implements CustomerService {
- CustomerDao customerDao =new CustomerDaoImpl();
- }
以上的代码表示:业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种依赖关系就是我们可以通过优化代码解决的。
还有如下面的代码:
我们的类依赖了MySQL的具体驱动类,如果这时候因为某些原因数据库的品牌从MySQL改为Oracle,那么需要通过改源码来修改数据库驱动。这显然不是我们想要的。
- public class JdbcDemo01{
- public static void main(String[] args) throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- //new com.mysql.jdbc.Driver()
- }
- }
当是我们学习JDBC时,是通过反射来注册驱动的,代码如下:
Class.forName("com.mysql.jdbc.Driver");
这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译。但是因为没有驱动类,所以不能运行。
不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。
解决这个问题也很简单,使用配置文件配置。
在实际开发中我们可以把所有的dao和service和controller对象使用配置文件配置起来,当启动服务器应用加载的时候,通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。
上面解耦的思路有2个问题:
1、存哪去?
分析:由于我们是很多对象,肯定要找个集合来存。这时候有Map和List供选择。
到底选Map还是List就看我们有没有查找需求。有查找需求,选Map。
在应用加载时,创建一个Map,用于存放controller,Service和dao对象。
我们把这个map称之为容器。
2、还是没解释什么是工厂?
工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:我们在获取对象时,都是采用new的方式。是主动的。

现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
它的作用只有一个:削减计算机程序的耦合。
Inversion of Control:控制权的转移,创建对象的权利由应用程序转移到容器称为控制反转
削减计算机程序的耦合(解除我们代码中的依赖关系)
创建Maven项目,resources 创建spring配置文件

pom.xml依赖
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>5.3.8</version>
- </dependency>
编写UserService类
- public class UserService {
- public UserService() {
- System.out.println("调用了UserService的构造方法");
- }
-
- public void add() {
- System.out.println("调用了UserService的add方法");
- }
-
- public void init() {
- System.out.println("调用了UserService的init方法");
- }
-
- public void destroy() {
- System.out.println("调用了UserService的destroy方法");
- }
- }
编写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">
- <!--Spring通过xml配置bean的方式来创建指定对象并管理起来容器-->
- <!--<bean> 作用告诉Spring创建对象并管理, id唯一标识, class指定类的全限定名(类名+包名)-->
- <!--spring通过反射创建对象-->
- <!--<bean id="userService" name="aaa" class="com.tjetc.service.UserService"></bean>-->
- <bean id="userService" class="com.tjetc.service.UserService"></bean>
- </beans>
编写测试类Test
- package com.tjetc;
-
- import com.tjetc.service.UserService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test {
- public static void main(String[] args) {
- //创建ClassPathXmlApplicationContext对象, 参数就是定义spring的配置文件
- //new ClassPathXmlApplicationContext成功之后,UserService配置的类已经被实例化了
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //通过context的getBean方法获取UserService对象
- UserService userService = context.getBean(UserService.class);
- //通过配置的id名称获取UserService对象
- UserService userService1 = (UserService) context.getBean("userService");
- // UserService userService1 =(UserService) context.getBean("aaa");
- //调用UserService的add方法
- userService.add();
- userService1.add();
- System.out.println(userService == userService1);//true
- }
- }
结果:

ApplicatioContext接口有两个基本的实现类:

ClassPathXmlApplicationContext : 加载类路径下的Spring配置文件。
FileSystemXmlApplicationContext : 加载本地磁盘下的Spring配置文件。
配置文件中的bean元素用于描述需要Spring容器管理的对象。其 class属性用于指被管理对象的完整类名。
id属性
是为Spring容器管理的对象起个名字。其使用ID约束:
name属性
也是为被Spring容器管理的对象起个名字。这样后续可以通过该名字从容器中获取对象。
没有ID中的那些约束(可以重复[不推荐]、可以出现特殊字符)
如果<bean>没有id的话,name可以当做id使用
举例
- <bean id="bookAction">
- <bean name="/loginAction" >
- <bean id="userService" name="aaa" class="com.tjetc.service.UserService">
此处的名称中由于有特殊字符,只能使用name属性。
结论:在定义bean的时候,推荐使用name属性来为bean指定名称
用于声明bean在Spring容器中的作用范围。其取值包括:
singleton : 默认值 单例的;默认在Spring容器启动的时候就会创建该实例。
prototype : 多例的;默认在从Spring容器中获取bean时才会创建该实例。
request : WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中。
session : WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中。
globalSession : WEB项目中,应用在Porlet环境。如果没有Porlet环境,那么globalSession相当于session。
singleton案例
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">
-
- <!-- scope="singleton" 是单例,容器启动创建配置的对象,context.getBean对候从容器中获取对象,每次getBean都是同一个对象-->
- <bean id="userService" class="com.tjetc.service.UserService" scope="singleton"></bean>
- </beans>
测试类:
- import com.tjetc.service.UserService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestTest {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //<bean>的配置项 scope="prototype" context.getBean时候创建对象,每次getBean都是新对象
- UserService userService1 = context.getBean(UserService.class);
- UserService userService2 = context.getBean(UserService.class);
- //==判断两个对象的地址是否相等
- System.out.println(userService1 == userService2);
- }
- }

prototype案例
测试类没有改变,只是修改application.xml的配置,运行结果也会有不同
- <!-- scope= "prototype" 是多例,容器启动 不创建配置的对象,context.getBean时候创建对象,每次getBean都是新对象-->
- <bean id="userService" scope="prototype" class="com.tjetc.service.UserService"></bean>

在bean被创建的时候,需要执行一些初始化的逻辑。可以指定bean中的一个方法为其初始化方法。这样Spring在创建完该对象之后,立即调用一下该初始化方法。
在bean对象销毁的时候,需要执行一些销毁逻辑。可以指定bean中的一个方法为其销毁方法。这样Spring容器在关闭之前并且销毁该对象的时候,会调用一下该销毁方法。
注意,此处的销毁方法必须是在容器正常关闭(即执行close方法)时,才会被执行。
init和destroy案例
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">
-
- <!--init-method 配置的对象创建成功之后, 会触发init-method配置的方法-->
- <!--destroy-method 配置的对象创建成功之后, 会触发destroy-method配置的方法-->
- <bean id="userService" class="com.tjetc.service.UserService" scope="singleton"
- init-method="init" destroy-method="destroy"></bean>
-
- </beans>
测试类:
- import com.tjetc.service.UserService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestTest {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- UserService userService = context.getBean(UserService.class);
- userService.add();
- //正常关闭
- context.close();
- }
- }
方式1:无参数的构造方法的方式
<bean id="userService" class="com.tjetc.service.UserService" scope="singleton" init-method="init" destroy-method="destroy"></bean>
方式2:静态工厂实例化的方式
- import com.tjetc.service.UserService;
-
- public class UserServiceFactory {
- public static UserService getBean() {
- //这里可以写很复杂的逻辑, 模拟业务
- return new UserService();
- }
- }
- <!--class配置的是工厂类,
- factory-method配置的是工厂类的静态方法,
- 让Spring调用工厂类的静态方法生产对象并管理-->
- <bean id="userService2" init-method="init" class="com.tjetc.factory.UserServiceFactory"
- factory-method="getBean"></bean>
测试类:
- import com.tjetc.service.UserService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- //创建Spring的上下文对象[调用了UserService的构造方法]
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //通过id值获取bean对象然后强转为UserService
- UserService userService2 = (UserService) context.getBean("userService2");
- //[调用了UserService的add方法]
- userService2.add();
- }
- }

方式3:实例工厂实例化的方式
- import com.tjetc.service.UserService;
-
- public class UserServiceFactory2 {
- /**
- * 实例方法,要调用次方法,必领先创建UserServiceFactory2的实例对象
- *
- * @return
- */
- public UserService getBean2() {
- return new UserService();
- }
- }
- <!--配置创建UserServiceFactory2的工厂实例对象并管理-->
- <bean id="userServiceFactory2" class="com.tjetc.factory.UserServiceFactory2"></bean>
-
- <!--配置调用UserServiceFactory2的工厂实例对象的getBean2的实例对象接收UserService对象并管理-->
- <bean id="userService3" factory-bean="userServiceFactory2" factory-method="getBean2"></bean>
测试类:
- import com.tjetc.factory.UserServiceFactory2;
- import com.tjetc.service.UserService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- //创建Spring的上下文对象[调用了两次UserService的构造方法, 因为配置了两次,id="userService2",id="userService3"]
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //获取工厂bean
- UserServiceFactory2 userServiceFactory2 = (UserServiceFactory2) context.getBean("userServiceFactory2");
- System.out.println(userServiceFactory2);
- //获取UserServiceBean
- UserService userService3 = (UserService) context.getBean("userService3");
- userService3.add();
- UserService userService33 = (UserService) context.getBean("userService3");
- //true,bean 单例 在配置,getBean多次也是一个对象
- System.out.println(userService3 == userService33);
- context.close();
- }
- }

定义Car
- public class Car {
- private String name;
- private int price;
-
- /**
- * 原则上 自定义了有参的构造方法, 最好加一个无参的构造方法, 防止容器启动调用构造方法出现问题
- */
- public Car() {
- }
-
- public Car(String name, int price) {
- this.name = name;
- this.price = price;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getPrice() {
- return price;
- }
-
- public void setPrice(int price) {
- this.price = price;
- }
-
- @Override
- public String toString() {
- return "Car{" +
- "name='" + name + '\'' +
- ", price=" + price +
- '}';
- }
- }
Spring配置Car
- <!--配置Car调用对应的构造方法-->
- <bean id="car" class="com.tjetc.entity.Car">
- <!--配置构造方法参数-->
- <constructor-arg name="name" value="保时捷"></constructor-arg>
- <constructor-arg name="price" value="1000000"></constructor-arg>
- </bean>
测试
- import com.tjetc.entity.Car;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- //通过类型获取bean
- Car car = context.getBean(Car.class);
- System.out.println(car);
- }
- }
基本类型的属性注入
Spring配置
- <!--简单类型属性注入-->
- <!--调用无参的构造方法-->
- <bean id="car1" class="com.tjetc.entity.Car">
- <!--使用属性设置值的前提条件:对应的类的属性要有set方法-->
- <property name="name" value="本田"></property>
- <property name="price" value="300000"></property>
- </bean>
测试
- import com.tjetc.entity.Car;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
- Car car1 = (Car) context.getBean("car1");
- System.out.println(car1);
- }
- }
对象类型的属性注入
定义Person
- package com.tjetc.entity;
-
- public class Person {
- private String name;
- private Car car;//自定义的Car类型
-
- public Person() {
- }
-
- public Person(String name, Car car) {
- this.name = name;
- this.car = car;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public Car getCar() {
- return car;
- }
-
- public void setCar(Car car) {
- this.car = car;
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "name='" + name + '\'' +
- ", car=" + car +
- '}';
- }
- }
Spring配置
- <!--复杂类型属性注入-->
- <bean id="person" class="com.tjetc.entity.Person">
- <property name="name" value="Jack"></property>
- <!-- 复杂类型, 使用ref来引用已经配置的bean的id值-->
- <property name="car" ref="car1"></property>
- </bean>
测试:
- import com.tjetc.entity.Person;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
- Person person = (Person) context.getBean("person");
- System.out.println(person);
- }
- }
![]()
db.properties
- jdbc.driverName=com.mysql.cj.jdbc.Driver
- jdbc.url=jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
- jdbc.username=root
- jdbc.password=123456
方案1:配置Spring的Bean PropertySourcesPlaceholderConfigurer
- <!--读取properties配置文件-->
- <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
- <property name="locations">
- <array>
- <value>classpath*:db.properties</value>
- </array>
- </property>
- </bean>
-
- <!--配置DbConfiguration, 使用使用属性注入法取到的properties 文件内容-->
- <bean id="dbConfiguration" class="com.tjetc.common.DbConfiguration">
- <!--${} 读取properties配置的key对应的值数据-->
- <property name="driverName" value="${jdbc.driverName}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
方案2:引入 context名称空间 。使用context中提供的 <context:property-placeholder> 指定它的 location 属性值,如果有多个文件,使用 逗号 分割。
- <?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
- http://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>引入并读取properties文件内容-->
- <context:property-placeholder location="classpath*:db.properties"></context:property-placeholder>
-
- <!--配置DbConfiguration, 使用使用属性注入法取到的properties 文件内容-->
- <bean id="dbConfiguration" class="com.tjetc.common.DbConfiguration">
- <!--${} 读取properties配置的key对应的值数据-->
- <property name="driverName" value="${jdbc.driverName}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
- </beans>
- public class Test12 {
- public static void main(String[] args) {
- // ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml,applicationContext.xml");
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
- DbConfiguration dbConfiguration = (DbConfiguration) context.getBean("dbConfiguration");
- System.out.println(dbConfiguration);
- }
- }
classpath:xx 这种写法,只会搜索到文件夹类型的下面的资源。不会搜索到jar包中的。 classpath*:xx 这种写法,都会搜索到。所以【推荐用这种】
定义CollectionDemo
- import java.util.Arrays;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
-
- public class CollectionDemo {
- private String[] strs;
- private List<String> list;
- private Map<String,String> map;
- private Properties properties;
-
- public String[] getStrs() {
- return strs;
- }
-
- public void setStrs(String[] strs) {
- this.strs = strs;
- }
-
- public List<String> getList() {
- return list;
- }
-
- public void setList(List<String> list) {
- this.list = list;
- }
-
- public Map<String, String> getMap() {
- return map;
- }
-
- public void setMap(Map<String, String> map) {
- this.map = map;
- }
-
- public Properties getProperties() {
- return properties;
- }
-
- public void setProperties(Properties properties) {
- this.properties = properties;
- }
-
- @Override
- public String toString() {
- return "CollectionDemo{" +
- "strs=" + Arrays.toString(strs) +
- ", list=" + list +
- ", map=" + map +
- ", properties=" + properties +
- '}';
- }
- }
Spring配置
- <bean id="collectionDemo" class="com.tjetc.entity.CollectionDemo">
- <!--数组类型的属性注入-->
- <property name="strs">
- <array>
- <value>张三</value>
- <value>李四</value>
- </array>
- </property>
-
- <!--集合类型的属性注入-->
- <property name="list">
- <list>
- <value>123</value>
- <value>456</value>
- </list>
- </property>
-
- <!--Map类型的属性注入-->
- <property name="map">
- <map>
- <entry key="username" value="bxbx"></entry>
- <entry key="password" value="9595"></entry>
- </map>
- </property>
-
- <!--properties类型的属性注入-->
- <property name="properties">
- <props>
- <prop key="xiao">肖</prop>
- <prop key="wang">王</prop>
- </props>
- </property>
-
- </bean>
测试
- import com.tjetc.entity.CollectionDemo;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
- CollectionDemo collectionDemo = (CollectionDemo) context.getBean("collectionDemo");
- System.out.println(collectionDemo);
- }
- }

随着项目越来越大,Spring的配置文件的内容也会越来越多。在实际的开发中,会将不同的配置定义在不同的xml文件中。有两种使用方式:
方式1:在创建容器的时候加载多个配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","propertiesContext.xml");
方式2:在主配置文件中包含其它配置文件(推荐使用)
<!-- 主配置文件applicationContext.xml中引入子配置文件 -->
<import resource="propertiesContext.xml"></import>
propertiesContext.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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- https://www.springframework.org/schema/context/spring-context.xsd">
-
- <!--读取properties配置文件-->
- <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
- <property name="locations">
- <array>
- <value>classpath*:db.properties</value>
- </array>
- </property>
- </bean>
-
- <!--<context:property-placeholder>引入并读取properties文件内容-->
- <!-- <context:property-placeholder location="classpath*:db.properties"></context:property-placeholder>-->
- </beans>
applicationContext.xml
- <!--引入其他的配置文件, 读取其它配置文件内容-->
- <import resource="propertiesContext.xml"></import>
-
- <!--配置DbConfiguration, 使用使用属性注入法取到的properties 文件内容-->
- <bean id="dbConfiguration" class="com.tjetc.common.DbConfiguration">
- <!--${} 读取properties配置的key对应的值数据-->
- <property name="driverName" value="${jdbc.driverName}"></property>
- <property name="url" value="${jdbc.url}"></property>
- <property name="username" value="${jdbc.username}"></property>
- <property name="password" value="${jdbc.password}"></property>
- </bean>
test
- import com.tjetc.common.DbConfiguration;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
- DbConfiguration dbConfiguration = (DbConfiguration) context.getBean("dbConfiguration");
- System.out.println(dbConfiguration);
- }
- }

主要解决的问题,在XML中一个一个对bean进行配置,开发效率也不高。
目的:
使用注解要先开启注解扫描的功能
- <!--配置组件的扫描com.tjetc本包及其子孙包下的所有的在类上标注有@Controller,@Service,@Repository,@Component注解的类,
- spring会把标注了这些注解的类当做你配置bean节点一样纳入spring容器管理-->
- <context:component-scan base-package="com.tjetc"></context:component-scan>
标注在类上,说明这是一个Spring组件。Spring中目前还提供了与 @Component 功能一致的其它三个衍生注解:
衍生的三个注解是为了让标注类本身的用途清晰,Spring在后续版本会对其增强
作用和XML配置文件的<bean>标签的实现功能一致;
使用注解注入的方式,可以不用提供set方法。
相关注解
@Value : 用于注入普通类型和String类型。
加在成员变量上——原理是反射赋值(破坏封装)
加在set方法上——原理是方法调用(推荐使用)
它可以使用spring中的spEL(也就是spring的el表达式),SpEl的写法:${表达式}
@Value使用
- package com.tjetc.common;
-
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Component;
-
- @Component("adminConstant")//小括号字符串是AdminConstant类的bean的名称
- public class AdminConstant {
- //@Value可以把配置数据注入到成员变量或者set×××方法, 前提条件:Spring要对类实例化并管理
- //@value 通过${} 读取key对应的value数据
- @Value("${img.base.path}")
- private String basePath;
- private String baseType;
- @Value("111111")
- private String abc;
-
- @Value("${img.base.type}")
- public void setBaseType(String baseType) {
- this.baseType = baseType;
- }
-
- public String getBasePath() {
- return basePath;
- }
-
- public void setBasePath(String basePath) {
- this.basePath = basePath;
- }
-
- public String getBaseType() {
- return baseType;
- }
-
- @Override
- public String toString() {
- return "AdminConstant{" +
- "basePath='" + basePath + '\'' +
- ", baseType='" + baseType + '\'' +
- ", abc='" + abc + '\'' +
- '}';
- }
- }
applicationContext.xml配置
- <!--读取properties类型的配置文件-->
- <context:property-placeholder location="classpath*:admin.properties"></context:property-placeholder>
admin.properties
- img.base.path=c:/image
- img.base.type=jpg,jpeg,gif,png
测试
- package com.tjetc;
-
- import com.tjetc.common.AdminConstant;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
- AdminConstant adminConstant = (AdminConstant) context.getBean("adminConstant");
- System.out.println(adminConstant);
- }
- }

@Autowired : 用于注入对象类型(自动装配)
默认按类型进行装配
按名称注入: @Qualifier——强制使用名称注入,一般不能单独使用和Autowired配合使用,当注入的对象类型有多个时,@Autowoired必须与@Qualifier一起使用
@Resource : 相当于@Autowired和@Qualifier一起使用
UserController.java
- package com.tjetc.controller;
-
- import com.tjetc.service.UserService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.stereotype.Controller;
-
- import javax.annotation.Resource;
-
- @Controller
- public class UserController {
- // @Autowired 按照类型从spring容器中查找到UserService的bean, 注入到成员变量
- // 依赖查找: 根据类型或者bean的名称, 在spring容器中查找相应类型的bean
- // 依赖注入: 根据依赖查找到的bean对象, 赋值到指定的成员变量或者set方法
- // 按照类型注入存在一个问题: spring容器里有很多个类型相同, 但不是同一个对象的bean, 按照类型注入会报错
- // @Autowired
- // @Qualifier ----强制使用名称注入, 一般不能单独使用, 要和Autowired配合使用
- // @Qualifier("userService1")
-
- // @Resource = @Autowired + Qualifier
- @Resource(name = "userService1")//依赖查找并注入,会拿到新的,打印构造方法和初始化
- // @Resource(name = "userService2")//单例, 不会打印出init, 单例体现在getBean(),不体现在配置上
- private UserService userService;
-
- /**
- * 添加
- */
- public void add() {
- /**
- * 调用UserService的add方法
- */
- userService.add();
- }
- }


applicationContext.xml配置
- <!--配置UserService对应的bean--><!--测试有两个不同对象但类型相同的bean-->
- <bean id="userService2" class="com.tjetc.service.UserService"></bean>
注解@Scope
相关注解
测试
- package com.tjetc.service;
-
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Service;
-
- import javax.annotation.PostConstruct;
- import javax.annotation.PreDestroy;
-
- @Service("userService1")
- //@Scope("singleton")//单例
- @Scope("prototype")//多利
-
- public class UserService {
- public UserService() {
- System.out.println("调用了UserService的构造方法");
- }
-
- public void add() {
- System.out.println("调用了UserService的add方法");
- }
-
- //@PostConstruct 相当于init-method
- @PostConstruct
- public void init() {
- System.out.println("调用了UserService的init方法");
- }
-
- // @PreDestroy 相当于destroy-method
- @PreDestroy//要关闭context.close();
- public void destroy() {
- System.out.println("调用了UserService的destroy方法");
- }
- }
- package com.tjetc;
-
- import com.tjetc.common.AdminConstant;
- import com.tjetc.controller.UserController;
- import com.tjetc.service.UserService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class Test12 {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
- UserService userService1 =(UserService) context.getBean("userService1");
- UserService userService2 =(UserService) context.getBean("userService1");
- //判断地址是否相等,不等,说明是两个对象,相等说明是一个对象
- System.out.println(userService1==userService2);//不加多例为true,加了多例为false
- context.close();
- }
- }

Aop思想

AOP(面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统-维护的-种技术。AOP是OOP的延续,是软件开发中的-个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Spring是解决实际开发中的一些问题:
AOP的常用应用场景
AOP最早由AOP联盟的组织提出的,制定了一套规范。Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。
Spring的AOP底层用到两种代理机制
JDK 的动态代理 : 针对实现了接口的类产生代理。
被代理的类必须要实现接口,才能产生代理对象。如果没有接口将不能使用动态代理技术。
Cglib 的动态代理 : 针对没有实现接口的类产生代理。应用的是底层的字节码增强的技术生成当前类的子类对象。
可以对任何 非final声明的 类生成代理。
Joinpoint(连接点) : 所谓连接点是指 那些被拦截到的点 。在Spring中,这些点指的是方法。
Pointcut(切入点) : 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知/增强) : 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知分为前置通知、后置 通知、异常通知、最终通知、环绕通知(切面要完成的功能)。
Aspect(切面) : 是切入点和通知(引介)的结合,即切入点+通知
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.9.7</version>
- </dependency>
- package com.tjetc.aop;
-
- import com.tjetc.common.DbDruidUtils;
- import org.aspectj.lang.ProceedingJoinPoint;
-
- /**
- * 切面类(自定义的业务增强类)
- */
- public class XMLTransactionPrint {
- public void doBefore() {
- //模拟业务
- System.out.println("前置通知");
- // System.out.println("开启事务");
- }
-
- public void doAfterReturning() {
- //模拟业务
- System.out.println("后置通知");
- // System.out.println("提交事务");
- }
-
- public void doAfterThrowing() {
- //模拟业务
- System.out.println("异常通知");
- // System.out.println("回滚事务");
- }
-
- public void doAfter() {
- //模拟业务
- System.out.println("最终通知");
- // System.out.println("关闭连接");
- }
-
- /**
- * 环绕通知对应的方法
- *
- * @param pjp 封装了 执行业务方法的相关信息
- * @return
- * @throws Throwable
- */
- public Object doRound(ProceedingJoinPoint pjp) throws Throwable {
- try {
- // System.out.println("环绕前置通知");
- System.out.println("开启事务");
- DbDruidUtils.beginTransaction();
- //执行业务类的方法
- Object result = pjp.proceed();
- // System.out.println("环绕后置通知");
- System.out.println("提交事务");
- DbDruidUtils.commit();
- return result;
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- // System.out.println("环绕异常通知");
- System.out.println("回滚事务");
- DbDruidUtils.rollback();
- throw throwable;
- } finally {
- // System.out.println("环绕最终通知");
- System.out.println("关闭连接");
- DbDruidUtils.closeConnection();
- }
- }
- }
- package com.tjetc.service;
-
- import org.springframework.stereotype.Service;
-
- /**
- * 已经存在的业务类, 也就是被代理的目标类
- */
- @Service
- public class PersonService {
- public void add() {
- System.out.println("调用了PersonService的add方法");
- }
-
- public void update() {
- //查询
- //更新库存 更新库存失败(没有出现异常),主动抛出异常
- //添加订单
- System.out.println("调用了PersonService的update方法");
- }
-
- public void delete() {
- System.out.println("调用了PersonService的delete方法");
- int i = 1 / 0;
- }
- }
- <?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
- https://www.springframework.org/schema/aop/spring-aop.xsd">
- <bean id="xmlTransactionPrint" class="com.tjetc.aop.XMLTransactionPrint"></bean>
- <bean id="personService" class="com.tjetc.service.PersonService"></bean>
-
- <!--配置aop-->
- <aop:config>
- <!--配置切面,切面=切点+通知-->
- <aop:aspect ref="xmlTransactionPrint">
- <!--配置切点-->
- <!--expression execution() 执行 固定-->
- <!--从左往右-->
- <!-- 第一个星号表示 方法的任何返回类型 包括void-->
- <!-- com.tjetc.service表示包-->
- <!-- 两个点表示包下直接的类和子孙包下的类, 如果是一个点表示包下直接的类-->
- <!-- 第二个星号, 表示任何名称的类-->
- <!-- 第三个星号, 表示任何名称的方法。 如add-->
- <!-- () 表示参数列表-->
- <!-- 小括号内的两个点表示0个或者1个或者多个参数类型-->
- <aop:pointcut id="mycut" expression="execution(* com.tjetc.service..*.*(..))"/>
- <!--配置通知-->
- <!--前置通知, 触发点是在调用业务方法之前被调用, 一定会被触发-->
- <!-- method 表示自定义增强的方法-->
- <!-- pointcut-ref 表示使用哪个切点-->
- <aop:before method="doBefore" pointcut-ref="mycut"></aop:before>
- <!-- 后置通知, 触发点是在调用业务方法之后, 不一定被触发, 因为业务方法会出现异常-->
- <aop:after-returning method="doAfterReturning" pointcut-ref="mycut"></aop:after-returning>
- <!-- 异常通知, 触发点是业务方法出现异常, 不一定被触发, 因为业务方法不一定会出现异常-->
- <aop:after-throwing method="doAfterThrowing" pointcut-ref="mycut"></aop:after-throwing>
- <!-- 最终通知, 触发点是finally, 一定会被触发-->
- <aop:after method="doAfter" pointcut-ref="mycut"></aop:after>
-
-
- <!--环绕通知-->
- <!-- <aop:around method="doRound" pointcut-ref="mycut"></aop:around>-->
-
- </aop:aspect>
- </aop:config>
-
- </beans>
测试类:
- package com.tjetc;
-
- import com.tjetc.service.PersonService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestAop {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAop.xml");
- PersonService personService = context.getBean(PersonService.class);
- personService.add();
- System.out.println("=========================");
- personService.update();
- System.out.println("=========================");
- personService.delete();
- }
- }
配置通知运行

环绕通知运行

开启环绕通知配置,注释前置通知、后置通知、异常通知、最终通知,进行测试
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.9.7</version>
- </dependency>
- <?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/context
- https://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop
- https://www.springframework.org/schema/aop/spring-aop.xsd">
-
- <!--注解扫描4大注解 @Controller,@Service,@Repository,@Component 纳入Spring管理-->
- <context:component-scan base-package="com.tjetc"></context:component-scan>
- <!--开启@Aspect注解的使用-->
- <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
- </beans>
@Aspect声明的类,和普通类一样可以添加属性和方法,还可以包含切入点、通知、引入。
Pointcut声明: 切入点声明包含两个部分:
1.签名:由一个名字和多个参数组成,必须返回void, 如:private void anyMethod(){}
2.切入点表达式 如:@Pointcut("execution(* com.tjetc.service..*.*(..))")
- package com.tjetc.aop;
-
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
-
- /**
- * 打开注解关闭环绕测试一次
- * 关闭注解打开环绕测试一次
- */
- @Component //纳入Spring管理
- @Aspect //切面类(增强类)
- public class AnnotationTransactionPoint {
- //定义一个空方法, 要求返回值为void, 作为切入点表达式
- // @Pointcut 用来编写切点表达式
- @Pointcut("execution(* com.tjetc.service..*.*(..))")
- private void anyMethod() {
-
- }
-
- //编写四大通知
- //前置通知
- // @Before("anyMethod()")//@Before 注解的值 写切点表达式对应的方法名称+(), 引入切点表达式
- public void doBefore() {
- System.out.println("前置通知");
- }
-
- // @AfterReturning("anyMethod()")
- public void doAfterReturning() {
- System.out.println("后置通知");
- }
-
- // @AfterThrowing("anyMethod()")
- public void doAfterThrowing() {
- System.out.println("异常通知");
- }
-
- // @After("anyMethod()")
- public void doAfter() {
- System.out.println("最终通知");
- }
-
- @Around("anyMethod()")
- public Object doRound(ProceedingJoinPoint pjp) throws Throwable {
- try {
- System.out.println("环绕前置通知");
- //执行业务方法
- Object result = pjp.proceed();
- System.out.println("环绕后置通知");
- return result;
- } catch (Throwable throwable) {
- System.out.println("环绕异常通知");
- throwable.printStackTrace();
- throw throwable;
- } finally {
- System.out.println("环绕最终通知");
- }
- }
- }
- package com.tjetc.service;
-
- import org.springframework.stereotype.Service;
-
- /**
- * 已经存在的业务类, 也就是被代理的目标类
- */
- @Service
- public class PersonService {
- public void add() {
- System.out.println("调用了PersonService的add方法");
- }
-
- public void update() {
- //查询
- //更新库存 更新库存失败(没有出现异常),主动抛出异常
- //添加订单
- System.out.println("调用了PersonService的update方法");
- }
-
- public void delete() {
- System.out.println("调用了PersonService的delete方法");
- int i = 1 / 0;
- }
- }
- package com.tjetc;
-
- import com.tjetc.service.PersonService;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestAopForAnnotation {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAopForAnnotation.xml");
- PersonService personService = context.getBean(PersonService.class);
- personService.add();
- System.out.println("=========================");
- personService.update();
- System.out.println("=========================");
- personService.delete();
- }
- }
配置环绕

环绕通知

开启环绕通知配置,注释前置通知、后置通知、异常通知、最终通知,进行测试