文章目录
1.1 简介
- spring : 春天 给程序员带来了春天
- 2002 ,首次推出了spring雏形。
- spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布1.0正式版
- Spring Framework创始人,著名作者。 Rod在悉尼大学不仅获得了计算机学位,同时还获得了音乐学位。更令人吃惊的是在回到软件开发领域之前,他还获得了音乐学的博士学位。 有着相当丰富的C/C++技术背景的Rod早在1996年就开始了对Java服务器端技术的研究。
- spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
- SSH:struct2 + Spring + Hibernate
- SSM:SpringMVC + spring + MyBatis
- 学习官方文档:https://docs.spring.io/spring-framework/docs/5.2.11.RELEASE/spring-framework-reference/core.html#spring-core
- 官网:https://spring.io/projects/spring-framework#overview
- 官方下载地址:https://repo.spring.io/release/org/springframework/spring/
- GitHub:https://github.com/spring-projects/spring-framework
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
-
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"> </beans>
1.2,优点
- spring 是一个开源的免费的框架(容器)
- spring是一个轻量级的,非侵入式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持十五处理,对框架整合支持
1.3,spring的组成
The following diagram shows a high-level view of how Spring works. Your application classes are combined with configuration metadata so that, after the ApplicationContext
is created and initialized, you have a fully configured and executable system or application.
![](https://i-blog.csdnimg.cn/blog_migrate/cf5e94636ae119124e6b6c2391c2950d.png)
1.4,扩展
![](https://i-blog.csdnimg.cn/blog_migrate/61f7f0cb4351805f0c9e7e6de83dd97e.png)
- spring boot
- 一个快速的开发脚手架
- 基于Springboot可以快速的开发单个微服务
- 约定大量配置
- spring cloud
- springcloud是基于Springboot实现的
因为现在大多数的公司都在使用Spring Boot进行快速的开发,学习springboot的前提需要掌握spring,springmvc。承上启下。
弊端:发展太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱
1.5 IOC理论(控制反转)
在面向对象的传统方式中,获取对象的方式通常是用new关键字主动创建一个对象。spring中的Ioc方式对象的生命周期有spring框架提供的ioc容器来管理,直接从IoC容器中获取一个对象,控制权从应用程序交给了IoC容器。理论上是借助于“第三方”实现具有依赖关系对象之间的解耦,如下图,即把各个对象类封装之后,通过IoC容器来关联这些对象类。这样对象于对象之间就通过IoC容器进行联系,而对象与对象之间就没有什么联系。
应用程序没有引入IoC容器之前,对象A依赖对象B,那么A对象在实例化或者运行到某一点的时候,自己必须主动创建对象B或者使用已经创建好的对象B,其中无论是创建还是使用自己创建的对象B控制权都在应用程序自身。如果应用程序引入IoC容器之后,对象A和对象B之间失去了直接联系,那么当对象A实例化和运行时,如果需要对象B,IoC容器就会主动创建一个对象B注入(依赖注入)到对象A所需要的地方。由此,对象A获得依赖对象B的过程,由主动行为变成被动行为,即把创建对象交给了IoC容器处理,控制权颠倒了过来,这就是所谓的控制反转。
在之前的业务中,用户的需求可能会影响我们原来的代码,我们需要更具用户的需求区修改原来的代码!如果程序代码量十分巨大,修改一次的成本代价回十分昂贵!
我们可以使用一个set接口实现,已经发生了革命性的变化
private UserDao userDao;
//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
之前,程序是主动创建对象!控制权在程序员手中
使用set注入,程序不在具有主动性,而是变成了被动的接受对象!
这种思想从本质上解决了问题,我们程序员不用再去管理对象的创建,系统的耦合性大大降低,可以更加专注在业务逻辑上
注意:事实上依赖注入和控制反转是对同一件事情的不同描述,从某个方面说,就是他们描述的角度不同。依赖注入是从应用程序的角度描述,即应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度描述,即容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。外部资源可以是外部文件或对象。
1.6 IoC创建对象的方式
-
使用无参构造函数创建对象,默认
-
使用有参构造函数创建对象。
-
下标赋值
<!-- 第一种赋值,下标赋值 --> <bean id="user" class="com.qijian.pojo.User"> <constructor-arg index="0" value="mahuahong"/> </bean>
-
类型赋值
<bean id="user" class="com.qijian.pojo.User"> <!-- 类型赋值--> <!-- 通过类型创建对象--> <constructor-arg type="java.lang.String" value="qijian"/> </bean>
-
通过参数名赋值
<bean id="user" class="com.qijian.pojo.User"> <!-- 通过参数名创建对象--> <constructor-arg name="name" value="柒间"/>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了。
-
2.1spring配置
-
别名,如果添加了别名也可以通过别名获取对象
<alias name="user" alias="newname"/>
-
Bean的配置
<!-- id : bean 的唯一标识,也就是相当于对象名 class : bean 对象所对应的全限定名 : 包+类 name : 也就是别名 可以同时取多个别名 --> <bean id="us" class="com.qijian.pojo.User" name="user,us1"/>
-
import 一般用于团队开发使用,它可以配置对各配置文件,导入合并为一个
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册不同的Bean中,我们可以用import将所有的bean.xml合并为一个。只用的时候直接使用总的(applicationContex.xml)即可。
<import resource="beans.xml"/> <import resource="beans1.xml"/>
3 依赖注入
3.1 构造器注入
3.2 Set方式注入【重点】
-
依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
-
【环境的搭建】
-
Address类
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 + '\'' + '}'; } }
-
Students类
public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> game; 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> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGame() { return game; } public void setGame(Set<String> game) { this.game = game; } 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; } public Student(){} @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address.toString() + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", game=" + game + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }
-
bean.xml配置
<bean id="address" class="com.qijian.pojo.Address"/> <bean id="student" class="com.qijian.pojo.Student"> <!-- 普通注入 value--> <property name="name" value="qijan"/> <!-- Beans注入 ref--> <property name="address" ref="address"/> <!-- 数组注入 --> <property name="books"> <array> <value>think in java</value> <value>计算机原理</value> <value>SSM从入门到精通</value> </array> </property> <!-- list--> <property name="hobbys"> <list> <value>写代码</value> <value>听歌</value> </list> </property> <!-- Map--> <property name="card"> <map> <entry key="身份证" value="000000000000"/> <entry key="银行卡" value="1111111111"/> </map> </property> <!-- Set--> <property name="game"> <set> <value>王者荣耀</value> <value>刺激战场</value> </set> </property> <!-- null--> <property name="wife"> <null/> </property> <!-- properties--> <property name="info"> <props> <prop key="Driver">222222</prop> <prop key="sex">男</prop> <prop key="name">qijian</prop> <prop key="pwd">root</prop> </props> </property> </bean>
-
测试类
import com.qijian.pojo.Student; 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"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); /*输出 Student{ name='qijan', address=Address{a ddress='null' }, books=[ think in java, 计算机原理, SSM从入门到精通 ], hobbys=[ 写代码, 听歌 ], card={ 身份证=000000000000, 银行卡=1111111111 }, game=[ 王者荣耀, 刺激战场 ], wife='null', info={ name=qijian, Driver=222222, sex=男, pwd=root } } */ } }
3.3,其他方式注入
需要注意的是c名命空间和p名命空间不能直接使用需要导入xml约束
c:
xmlns:c="http://www.springframework.org/schema/c"
p:
xmlns:p="http://www.springframework.org/schema/p"
User类
package com.qijian.pojo;
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
-
p名命空间注入
** The p-namespace lets you use the
bean
element’s attributes (instead of nested<property/>
elements) to describe your property values collaborating beans, or both.P-namespace **
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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.qijian.pojo.User" p:name="qijian" p:age="18" /> </beans>
-
c名命空间注入(通过构造器注入)
Similar to the XML Shortcut with the p-namespace, the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring the constructor arguments rather then nested
constructor-arg
elements. **与 p 名称空间的 XML Shortcut 类似,Spring 3.1中引入的 c 名称空间允许内联属性配置构造函数参数,而不是嵌套的构造函数-arg 元素。 **
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="com.qijian.pojo.User"> <constructor-arg name="name" value="柒间"/> <constructor-arg name="age" value="19"/> </bean> //c名命空间注入 <bean id="user1" class="com.qijian.pojo.User" c:name="qijianc" c:age="18"/> </beans>
c名命空间注入需要由有参构造函数
*** 注意 ***
Null and Empty String Values
//Spring 将属性和类似属性的空参数视为空字符串。下面的基于 xml 的配置元数据片段将 email 属性设置为空 String 值(“”)。
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
上面相当于String email=“”;
//The <null/> element handles null values
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
上面的注入等价于String email = null;
3.4 ,bean的作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MfnYWCMc-1614954831564)(C:\Users\QiJian\AppData\Roaming\Typora\typora-user-images\Snipaste_2020-11-28_20-33-00.png)]
1,单例模式(spring默认机制)
只管理单一 bean 的一个共享实例,对 ID 或 ID 与 bean 定义相匹配的 bean 的所有请求都将导致 Spring 容器返回一个特定 bean 实例。
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
下面的图片展示了单例模式的工作原理:
![](https://i-blog.csdnimg.cn/blog_migrate/496a65c2677ecb7939e758a6384bd823.png)
2,原型模式
The non-singleton prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made. That is, the bean is injected into another bean or you request it through a getBean()
method call on the container. As a rule, you should use the prototype scope for all stateful beans and the singleton scope for stateless beans.
Bean 部署的非单例原型范围导致每次对特定 bean 发出请求时都会创建一个新的 bean 实例。也就是说,bean 被注入到另一个 bean 中,或者您通过容器上的 getBean ()方法调用请求它。通常,您应该为所有有状态 bean 使用原型范围,为无状态 bean 使用单例范围。
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
下图说明了 Spring 的原型范围:
![](https://i-blog.csdnimg.cn/blog_migrate/7038d885dd0c2f4fe5dd6d442a9982f4.png)
注意:每次从容器中get的时候,都会产生一个新的对象!
3,其余的request,session,application,这些只能在web开发中使用到!
4,bean的自动装配
环境:
Cat.class
package com.qijan.pojo;
public class Cat {
public void shout(){
System.out.println("miao~~~");
}
}
Dog.class
package com.qijan.pojo;
public class Dog {
public void shout(){
System.out.println("wang~~");
}
}
People.class
package com.qijan.pojo;
import com.qijan.pojo.Cat;
import com.qijan.pojo.Dog;
public class People {
private String name;
private Cat cat;
private Dog dog;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
"\n, cat=" + cat +
"\n, dog=" + dog +
'}';
}
}
bean.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="cat" class="com.qijan.pojo.Cat"/>
<bean id="dog" class="com.qijan.pojo.Dog"/>
<!-- <bean id="people" class="com.qijan.pojo.People">-->
<!-- <property name="name" value="qijian"/>-->
<!-- <property name="dog" ref="dog"/>-->
<!-- <property name="cat" ref="cat"/>-->
<!-- </bean>-->
<!-- 自动装配 byName:会自动在容器上下文查找,和自己对象set方法后面的值对应的bean id-->
<bean id="people" class="com.qijan.pojo.People" autowire="byName">
<property name="name" value="qijian"/>
</bean>
</beans>
测试类
import com.qijan.pojo.People;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.getCat().shout();
people.getDog().shout();
System.out.println(people.toString());
}
}
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文寻找,并自动给bean装配属性!;
在spring中有三种装配方式
- 在xml中显示的配置
- 在Java中显示的配置
- 隐式的自动装配bean【重要】
4.1,ByName自动装配
<bean id="cat" class="com.qijan.pojo.Cat"/>
<bean id="dog" class="com.qijan.pojo.Dog"/>
<!-- <bean id="people" class="com.qijan.pojo.People">-->
<!-- <property name="name" value="qijian"/>-->
<!-- <property name="dog" ref="dog"/>-->
<!-- <property name="cat" ref="cat"/>-->
<!-- </bean>-->
<!-- 自动装配 byName:会自动在容器上下文查找,和自己对象set方法后面的值对应的bean id-->
<bean id="people" class="com.qijan.pojo.People" autowire="byName">
<property name="name" value="qijian"/>
</bean>
4.2 byType自动装配
<bean class="com.qijan.pojo.Dog"/>
<bean class="com.qijan.pojo.Cat"/>
<!-- 自动装配 byType: 会自动在容器上下文查找,和自己对象属性类型相同的bean
可以不用设置bean id
-->
<bean id="people" class="com.qijan.pojo.People" autowire="byType">
<property name="name" value="qijian"/>
</bean>
小结:
- byName:需要保证bean的id的唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- byType:需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
4.4,使用注解实现自动装配
jdk1.5 开始支持注解,spring2.5开始支持注解
要使用注解须知:
-
导入约束
-
配置注解支持
您可以将它们注册为单独的 bean 定义,但也可以通过在基于 xml 的 Spring 配置中包含以下标记来隐式注册它们(注意包含上下文名称空间) :
<?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>
@Autowired
直接在属性上使用即可!也可以在set方法使用!
使用Autowired我们可以不用编写Set方法,前提是你的这个自动装配的属性在(spring)容器中存在,且符合名字byname!
@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired{ boolean required() default true; }
代码测试:
//使用在属性上
// 如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 // @Autowired(required = false) @Autowired private Cat cat; public People(@Nullable Cat cat) { this.cat = cat; }
//对构造函数应用@autowired 注释,如下面的例子所示:
public class People{ private String name; ... @Autowired public void People(String name){} ... }
//可以将@autowired 注释应用于传统的 setter 方法,如下面的示例所示:
public class People{ private String name; ... @Autowired public void setName(String name){ this.name = name; } }
//还可以将注释应用于具有任意名称和多个参数的方法等。
如果@Autowired自动装配的环境比较复杂,自动装配无法通过注解【@AutoWired】完成的时候,我们可以使用@Qualifier(value=“newcatName”)去配合@Autowired的使用,指定一个唯一的bean对象注入
public class People{ @Autowired @Qualifier(value="newcatname") private Cat cat; @Autowired @Qualifier(value="nawdogname") private Dog dog; ... }
注意:@Qualifier于@Autowired注解配合使用,会将默认的按bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的指定。
从 Spring Framework 4.3开始,如果目标 bean 从一开始就只定义一个构造函数,那么在这样的构造函数上就不再需要@autowired 注释了。但是,如果有多个构造函数可用,并且没有主/默认构造函数,那么至少有一个构造函数必须用@autowired 注释,以便指示容器使用哪个构造函数。有关详细信息,请参阅关于构造函数解析的讨论。
-
@resource功能比较强大,但是使用的较少
public class People{ @Resource(name="cat2") private Cat cat; @Resource private Dog dog; ... }
注意:
@Resource和@Autowired的区别
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired通过byName的方式实现
- @Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就会报错!
- 执行的顺序不同:@Autowired
5,使用注解开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入,使用注解需要导入context约束,增加注解的支持!
-
bean
-
属性如何注入
//java文件
package com.qijian.pojo; import org.springframework.stereotype.Component; @Component public class User { private String name = "qijian"; @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
//配置文件
<?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="com.qijian.pojo"/> <!-- 注解--> <context:annotation-config/> </beans>
-
衍生注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
-
dao【@Repository】
-
service【@Service】
-
controller【@Controller】
这四个注解功能时一样的,都是代表将莫格1类注册到Spring中
-
-
自动装配
@Autowired,@Qualifier(value =“XXX”),@Resource
-
作用域
@Scope 例如:@Scope(“prototype”) 写在类的上面
@Component @Scope("prototype") public class People{ // @Value("qijian") private String name; //相当于<property name="name" value="qijian"> @Value("qijian")//写在此处与写在属性上面时一样的,但是如果复杂的注入时不建议使用 public void setName(String name){ this.name=name; } }
-
小结:
xml与注解
-
xml更加万能,适用于任何场合!维护简单方便
-
注解不是自己类使用不了,维护相对复杂!
-
最佳实践:
-
xml用来管理bean;
-
注解只负责完成属性的注入;
-
使用过程中我们需要注意,要让注解生效,就必须开启注解的支持
<!-- 指定扫描的包,这个包下的注解会生效--> <context:component-scan base-package="com.qijian.pojo"/> <!-- 注解--> <context:annotation-config/>
注意:
使用注解开发时,UerControllerDD user = context.getBean(“uerControllerDD”,UerControllerDD.class);前面的参数(bean的名字)需要最从一定的格式不然就不能够从容器中拿到对应的bean。只需要把该类的第一个字母小写放在其中就可以了。并且如果出现下面的情况那么注入的属性的值时由注解决定的。
@Value("柒间")
private String name = "qijan";
6,使用Java的方式配置Spring
qijianConfig.class
package com.qijian.config;
import com.qijian.dao.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
//这个也会被spring容器托管,注册到容器中,因为它本身就是一个@Conponent
//@Configuration代表这个是一个配置类,就和我们之前的beans.xml
@Configuration
@ComponentScan("com.qijian.dao")
@Import(qijianConfig2.class)//把qijianConfig2配置类导入
public class qijianConfig {
//注册一个bean,相当于我们之前的bean标签
// 这个方法的名字,就相当于bean标签的id属性
// 这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User getUser(){
return new User();//返回注入到bean的对象
}
}
qijianConfig.class
package com.qijian.config;
public class qijianConfig2 {
}
User.class
package com.qijian.dao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//这里这个注解的意思就是说明这个类被spring接管了,注册到了容器中
@Component
public class User {
private String name ;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
@Value("qijian")
public void setName(String name) {
this.name = name;
}
}
MyTest.class
import com.qijian.config.qijianConfig;
import com.qijian.dao.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void Test1(){
//如果完全使用了配置类方式去做,我们只能通过AnnotationConfig上下文来获取容器,通过配置文件获取class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(qijianConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user.toString());
}
}
7,代理模式
角色分析
-
抽象角色:一般会用接口或者抽象类来解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
-
客户:访问代理对象的人
代码步骤:
-
接口
public interface Rent { public void rent(); }
真实角色:
public class Host implements Rent {
public void rent() {
System.out.println("出租房子");
}
}
-
代理角色:
public class Proxy { private Host host; public Proxy(){} public Proxy(Host host){ this.host=host; } public void rent(){ seeHouse(); host.rent(); heTong(); fare(); } //看房 public void seeHouse(){ System.out.println("中介看房"); } // public void heTong(){ System.out.println("签订和同"); } //收中介费 public void fare(){ System.out.println("收中介费"); } }
-
客户段访问代理角色
public class Client { public static void main(String[] args) { //房东出租房子 Host host = new Host(); // 代理,中介帮房东出租,代理角色一般会有些附属操作! Proxy proxy = new Proxy(host); //不用面对房东直接找中介,租房就可以了 proxy.rent(); } }
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用区关注一些公共业务
- 公共业务交给代理角色!实现了业务的分工!
- 公共业务发生拓展的时候,方便集中管理!
缺点:
- 一个真实的角色就会产生一个代理的角色;代码量会翻倍~开发效率变慢
8,AOP
8.1 什么是AOP
AOP全称是Aspect Oriented Programming,及面向切面编程(也称面向方面编程),是面向对象编程(OOP)的一种补充,目前已经成为一种比较成熟的编程方式。
AOP出现的原因:在传统的业务处理代码中,通常都会进行事务处理,日志记录的操作。OOP可以通过组合或继承的方法实现代码的重用,但如果要实现某个功能(如日志的记录),相同的代码仍然会分散到各个方法中去。这样,如果想关闭某个功能,或者对其修改,就必须修改所有相关的方法。这不但参加了开发人员的工作量,而且提高了代码的出错率。为了解决这个问题,AOP的思想随之产生。AOP采取横向抽取的机制,将分散在各个方法中的代码提取出来,然后在程序编译或运行时再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取的方式,采用传统的OOP思想是无法办到的,因为OOP只能实现父子关系的纵向重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。
8.2 AOP在spring中的作用
在AOP的思想中,通过Aspect(切面)可以在不同类的方法中加入事务,日志,权限和异常等功能。
AOP的使用使开发人员在编写业务逻辑的时候可以专心于核心业务,而不用过多的关注其他业务逻辑的实现,这不但提高了开发的效率,而且提高了代码的可维护性。
总的来说,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
-
横切关注点:跨越应用程序多个模块的方法或功能。及是与我们的业务逻辑无关,但是我们需要关注的部分就是横切关注点:如日志安全,缓存,事务等等
-
切面(Aspect):横切关注点被模块化的特殊对象。即它的一个类
-
通知(Advice):切面必须要完成的工作,即它是类的一个方法
-
目标(Target):被通知对象
-
代理(Proxy):向目标对象应用通知后创建对象
-
切入点(PointCut)切面通知执行的“地点”的定义
-
连接点(JointPoint):与切入点匹配额的执行点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MBVCdQBf-1619604879564)(C:\Users\QiJian\Pictures\Typora\point.png)]
8.3 使用spring实现AOP
**【重点】**使用AOP,需要导入一个依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
<scope>runtime</scope>
</dependency>
方式一:使用spring的API接口
导入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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<!-- bean definition & AOP specific configuration -->
</beans>
搭建如下:
//UserService接口
package com.qijian.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
UserService接口的实现类
//UserServiceImpl类
package com.qijian.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 select() {
System.out.println("查询了一个用户");
}
}
Log类
package com.qijian.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log 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类
package com.qijian.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//执行方法后调用
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+ method.getName()+"方法,返回结果为:"+returnValue);
}
}
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
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-->
<bean id="userService" class="com.qijian.service.UserServiceImpl"/>
<bean id="log" class="com.qijian.log.Log"/>
<bean id="afterLog" class="com.qijian.log.AfterLog"/>
<!--注册bean-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--1, 配置切入点 -->
<!--
id:用于定义切入点的唯一标识 expression:用于指定切入点关联的切入点表达式。
expression=“execution(* ..*.*(..))”;//需要注意的是第一个*与包之间有一个空格
expression:用来匹配执行方法的连接点
第一个*表示匹配任意的方法的返回值
第一个..表示service包及其子包
第二个*表示所有类
第三个*表示所有方法
第二个..表示方法的任意参数个数
-->
<aop:pointcut id="pointcut" expression="execution(* com.qijian.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
注意:配置通知的常用属性如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2rzBfyM-1619604879566)(C:\Users\QiJian\Pictures\Typora\A1CF9B5A2C39ABB632A54D68E32B98BD.png)]
父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>springaop</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-09-aop</module>
</modules>
<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>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
</dependencies>
</project>
子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>springaop</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-09-aop</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
方式二:自定义类实现AOP【主要定义切面】
自定义类:
package com.qijian.diy;
public class DiyPointCat {
public void before(){
System.out.println("========方法执行前========");
}
public void after(){
System.out.println("=========方法执行后========");
}
}
applicationContext.xml配置
<bean id="diy" class="com.qijian.diy.DiyPointCat"/>
<aop:config>
<!--自定义切面-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* com.qijian.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:使用注解实现【使用注解方式实现AOP】
基于XML的声明式AspectJ实现AOP编程虽然便捷,但是要在Spring文件中配置大量的代码信息。为了解决这个问题AspectJ框架为AOP的实现提供了一套注解,用以取代spring配置文件中为实现AOP功能所配置的臃肿代码。
在方式二的环境中添加一个类:
package com.qijian.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//使用注解方式显现AOP
@Aspect //标注这个类是一个切面e
public class AnnotionPointCut {
@Before("execution(* com.qijian.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("========方法执行前========");
}
@After("execution(* com.qijian.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====方法执行后====");
}
// 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.qijian.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
Signature signature = jp.getSignature();//获得签名
System.out.println("signature : " + signature);
//执行方法
Object proceed = jp.proceed();
System.out.println("环绕后!");
}
}
以上需要注意,注解开发需要两个依赖如下,虽然说aspectjweaver包含了aspectjrt,但是不知道为什么写代码的过程中没有aspectjrt这个依赖就是不能实现注解开发(此处有待商榷待找到原因后再来记录)。
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
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
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-->
<bean id="userService" class="com.qijian.service.UserServiceImpl"/>
<bean id="log" class="com.qijian.log.Log"/>
<bean id="afterLog" class="com.qijian.log.AfterLog"/>
<!-- 方式三 使用注解开发-->
<bean id="annotionPointCut" class="com.qijian.diy.AnnotionPointCut"/>
<!-- 开启注解支持-->
<aop:aspectj-autoproxy />
</beans>
测试类:
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService)context.getBean("userService");
userService.add();
}
}
结果:
环绕前
signature : void com.qijian.service.UserService.add()
========方法执行前========
增加了一个用户
=====方法执行后====
环绕后!
博客园同步更点击这里