Spring是一个轻量级的、非入侵式的框架!(开源、免费)
控制反转(IOC),面向切面编程(AOP)
支持事务的处理,对框架整合的支持
导包
组成
扩展
Spring Boot 一个快速开发的脚手架
基于SpringBoot可以快速的开发单个微服务 约定大于配置
Spring Cloud 基于SpringBoot实现
IOC理论推导
控制反转IOC是一种设计思想,DI(依赖注入)是实现IOC的一种方法。
在没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制。
控制反转是一种通过(描述XML或者注解)并通过第三方去生产或获取特定对象的方式。在spring中实现控制反转的时IOC容器,实现方法是依赖注入。
采用XML方式配置Bean的时候,Bean的定义信息是和现实分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注释的形式定义在实现类中,从而达到了零配置的目的。
private UserDao userDao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao){
this.userDao=userDao;
}
使用set注入后,不用再去管理对象的创建。系统的耦合性大大降低,可以更加专注的在业务的实现上。这是IOC的原型。
Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象。
HelloSpring
创建bean文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xm1ns="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">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型()
Hello hello = new Hello();
id = 变量名
class = new 的对象
property 相当于给对象中的属性设置一个值
-->
<bean id="hello" class="com.pojo.Hello">
<property name="str" vaiue="Spring"/>
</bean>
</beans>
测试
public class MyTest {
public static void main(String[] args) {
//解析bean文件,生成管理相应的bean对象
ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml");
//现在对象都在spring中的管理了,要使用时,直接去里面取出来就可以。
//getBean:参数即为spring配置文件中bean的id
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
Hello对象由Spring创建,hello对象的属性是由Spring容器设置的
IOC创建对象的方式
使用无参构造创建对象,默认
使用有参构造创建对象
1.下标赋值
//第一种,下标赋值
<bean id="user" class="com.pojo.User">
<constructor-arg index="0" vaiue="zxy"/>
</bean>
2.通过类型创建
//第二种,通过类型创建,不推荐使用
<bean id="user" class="com.pojo.User">
<constructor-arg type="java.lang.String" vaiue="zxy"/>
</bean>
3.通过参数名设置
//第二种,直接通过参数名来设置
<bean id="user" class="com.pojo.User">
<constructor-arg name="name" vaiue="zxy"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了
Spring配置说明
别名
<!--别名, 如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="userNew"/>
Bean的配置
<!--
id : bean的唯一 标识符,也就是相当于我们学的对象名
class : bean 对象所对应的全限定名:包名 +类型
name :也是别名,而且name可以同时取多个别名
-->
<bean id="userT" class="com. kuang. pojo.UserT" name="user2 u2,u3;u4">
<p roperty name="name" value=" zxy"/>
</bean>
Import
一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
假设现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,这时可以利用import将所有人的beans.xml合并为一个总的
<import resouse=”beans.xml”>
<import resouse=”beans2.xml”>
<import resouse=”beans3.xml”>
使用的时候,直接使用总的配置就可以了
依赖注入
Set方式注入
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性,由容器来注入
环境搭建
1.复杂类型
pub1ic class Address {
private String address;
pub1ic string getAddress() {
return address;
pub1ic void setAddress(String address) {
this. address = address;
}
}
2.真实测试对象
pub1ic class Student {
private String name;
private Address address;
private String[] books;
private List<string> hobbys ;
private Map<String,string> card;
private Set<String> games;
private String wife;
private Properties info;
}
3.Beans,XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xm1ns="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="student" class="com.pojo.User">
<property name="name" vaiue="zxy"/>
</bean>
</beans>
4.测试类
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 .getAddress());
}
}
完善后的bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xm1ns="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="address" class="com.pojo.Address">
<property name="address" value"xian">
</bean>
<bean id="student" class="com.pojo.Student">
<!--普通注入,value-->
<property name="name" value="zxy"/>
<!--Bean注入,ref-->
<property name="address" value="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="身份证" value="1232321314"/>
<entry key="银行卡" value="3423435234"/>
</map>
</property>
<!--Set-->
<property name="games">
<set>
<value>LOL</value>
<value>gupg</value>
</set>
</property>
<!--null-->
<property name="wife">
<null/>
</property>
<!--properties-->
<property name="info">
<props>
<prop key="driver">1239</prop>
<prop key="url">www.dasfdafarf</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
其他方式注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xm1ns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.W3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!--p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.pojo.User" p:name="zxy" p:age="22"/>
<!--c命名注入,通过构造器注入:construct-args-->
<bean id="user" class="com.pojo.User" c:name="zxy" c:age="22"/>
注意:p命名和 c命名空间不能直接使用,需要导入xml约束
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
Bean的作用域
1、单例模式(Spring默认机制)
<bean id="user2" class="com.pojo.User" c:name="zxy" c:age="22" scope=”singleton”/>
2、原型模式:每次从容器中get的时候,都会产生一个新对象!
<bean id="accountService" class="com.something.DefaultAccountService" scope=”prototype”/>
3、其余的request、session、spplication、这些只能在web开发中使用到
Bean的自动装配
在Spring中有三种装配的方式
- 在xml中显示的配置
- 在java中显示的配置
- 隐式的自动装配bean
ByName自动装配
<!--byname:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid! -->
<bean id="people" class="com.pojo.People" autowire="byName">
<property name="name" vaiue="zxy"/>
</bean>
ByType自动装配
<bean class="com.pojo.Cat" />
<bean class="com.pojo.Dog" />
<!--bytype:会自动在容器上下文中查找,和自己对象属性类型相同的bean! -->
<bean id="people" class="com.pojo.People" autowire="byName">
<property name="name" vaiue="zxy"/>
</bean>
小结:
byname需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
byname需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。
当注入容器存在多个同一类型的对象时,通过byName进行装配。
当注入容器对象的类型只有一个时,通过byType进行装配.
使用注解实现自动装配
使用注解
- 导入约束:context约束
- 配置注解的支持:context:annotation-config
<?xml version="1.0" encoding="UTF-8"?>
<beans xm1ns="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
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
@Autowired
直接在属性上使用即可,也可以在set方式上使用。
使用Autowired可以不用编写set方法,前提是自动装配的属性在IOC容器中存在,且符合名字byname
pub1ic class People {
//如果显示定义了Autowi red的requi red属性为false,说明这个对象可以为nu11,否则不允许为空
@Autowi red(required = false)
private Cat cat;
@Autowi red
private Dog dog;
private String name;
}
@Resource注解
pub1ic class People {
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
}
@Resource和@ Autowired的区别:
都是用来自动装配的,都可以放在属性字段上;
@ Autowired通过byType的方式实现,而且必须要求这个对象存在;
@ Resource默认通过byname的方式实现,如果找不到名字,则通过byType实现! 如果两个都找不到的情况下,就报错;
执行顺序不同: @ Autowired 通过byType的方式实现;@ Resource默认通过byname的方式实现。
使用注解开发
@Component 组件,
放在类上,说明这个类被Spring管理了。等价于<bean id=”user” class=”com.pojo.User”>
@Component 的衍生注解,按照mvc三层架构分层:
Dao【@Repository】 service【@Service】 controller【@Controller】
这四个注解功能一样,都代表将某个类注册到Spring中,装配Bean
@Value(“zxy”) 属性注入,相当于<property name=”name” value=”zxy”>
Xml与注解:
Xml更加万能,使用于任何场合,维护简单方便。
注解不是自己类使用不了,维护相对复杂。
Xml与注解的最佳实践:Xml用来管理bean;注解只负责完成属性的注入。
使用过程中注意,让注解生效需要开启注解的支持。
<!--指定要扫描的包,这个包下的注解就会生效-->
<context :component-scan base-package="com"/>
<context :annotati on-config/>
使用JavaConfig实现配置
实体类
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
@Component
pub1ic class user {
private String name;
pub1ic string getName(){
return name ;
}
@Value ("zxy") //属性注入值
pub1ic void setName(String name) {
this.name = name ;
}
@override
pub1ic String toString() {
return "User{"+
"name='" + name + '\'' +
'}';
}
}
配置文件
//这个也会Spring容器托管,注册到容器中,因为他本来就是-个@Component
// @Configuration代表这是一 个配置类,就和我们之前看的beans.xmL
@Configuration
@ComponentScan("com.kuang.pojo")
@Import(KuangConfig2.class)
public class KuangConfig {
//注册一个bean,就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User user(){
return new User(); //就是返回要注入到bean的对象!
}
}
测试类
pub1ic class MyTest {
pub1ic static void main(String[] args) {
//如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
App1icati onContext context = new Annotati onConfigApplicati onContext (KuangConfig.class);
User getuser = (User) context. getBean("user");
System. out. print1n (getUser . getName());
}
}
代理模式
代理模式好处:
·可以使真实角色的操作更简洁,不用去关注一些公共的业务
·公共业务交给代理角色,实现了业务的分工
·公共业务发生扩展的时候,方便集中管理
缺点:一个真实角色就会产生一个代理角色;代码量会翻倍~开发效率会变低
静态代理模式
角色分析
·抽象角色:一般会使用接口或者抽象类来解决
·真实角色:被代理的角色
·代理角色:代理真实角色,代理真实角色后一般会做一些附属操作
·客户:访问代理对象的人
代码步骤:
1、接口:
//租房
pub1ic interface Rent {
public void rent();
}
2、真实角色:
//房东
pub1ic class Host implements Rent {
public void rent() {
System. out.print1n("房东要出租房子! ");
}
}
3、代理角色:
public class Proxy implements Rent {
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.print1n("中介带你看房");
}
//签合同
public void hetong(){
System.out.print1n("签租赁合同");
}
//收中介费
public void fare(){
System.out.print1n("收中介费");
}
}
4、客户端访问代理角色:
pub1ic class client {
pub1ic static void main(String[] args) {
//房东要租房子
Host host = new Host();
//代理,中介帮房东租房子,但是呢?代理角一般会有一些附属操作!
Proxy proxy = new Proxy(host);
//你不用面对房东,直接找中介租房即可!
proxy.rent();
}
动态代理模式
动态代理的代理类是动态生成的,主要分为两大类:基于接口的动态代理;基于类的动态代理
·基于接口---JDK动态代理
·基于类:cglib
·java字节码实现:javasist
一个动态代理类代理的使一个接口,一般就是对应的一类业务
一个动态代理类可以代理多个类,只要是实现了用一个接口即可
AOP
面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP在Spring中的作用:提供声明式事务;允许用户自定义切面
·横切关注点:跨越应用程序多个模块的方法或功能。与业务逻辑无关但是需要关注的部分就是横切关注点。如日志、安全、缓存、事务等
·切面:横切关注点别模块化的特殊对象。即,一个类。
·通知:切面必须要完成的工作。即,类中的方法。
·目标:被通知的对象。
·代理:向目标对象应用通知之后创建的对象
·切入点:切面通知 执行的“地点”的定义
·连接点:与切入点匹配的执行点
使用Spring实现AOP
导入依赖包
<!-- https://mvnrepository. com/artifact/org. aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用Spring的API接口【主要是SpringAPI接口实现】
<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
<!--切入点: expression: 表达式,execution( 要执行的位置! *****)-->
<aop:pointcut id="pointcut"
expression="execution(*com.kuang.service. UserServiceImpl.*(..))"/>
<!--执行环绕增加!-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
方式二:自定义来实现AOP【主要是切面定义】
<!--方式二: 自义式-->
<bean id= "diy" class="com.kuang.diy.DiuPointCut"/>
<aop:config>
<!--自定义切面,ref 要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point"
expression="executron(*com.kuang.service.UserServrcelmpl.*(..))"/>
<!--通知-->
<aop:before method="before” pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:注解实现
<!--方式三-->
<bean id="annotationPointCut" class="com.kuang.diy.AnnotationPointCut"/>
<!--开启注解支持! JDK(默认proxy-target-class="false") cglib (proxy-target -class="true") -->
<aop: aspectj-autoproxy/>