【Spring】了解Spring及注解
Spring
是一个IOC和 AOP(面向切面编程) 的开源框架
IOC (Inverse Of Control ) 控制反转
指创建依赖对象控制权的转换,在原来系统中,依赖对象的创建,是需要调用者来负责维护的;采用了控制反转的系统,依赖的对象创建,将不再由调用者来维护,而是由容器负责维护;
class Dao{ } class Service{ Dao dao = new Dao(); // 通过 new 来创建依赖对象的实例 Dao dao = DaoFactory.getDao(); // 通过工厂的方式获取对象 }
DI(Dependency Inject)依赖注入
与 IoC 描述的是同一件事,只不过描述这件事的角度不同,从调用者的角度来看,是控制反转,从 容器的角度来看,是依赖注入;
好莱坞法则:Don't call me,We will call you!
AOP(Aspect-OrientedProgramming)面向切面编程
在程序开发中主要用来解决一些系统层面上的问题,比如日志收集,事务管理,权限,缓存,对象池管理等
AOP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。
便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
概念
切面(Aspect): 是一个横切性的关注点的抽象,实质上与类是同一个级别的概念,只不过关注点不同;类是对象的抽象
连接点(Joinpoint):程序执行过程中某个特定的点,连接切面与程序执行的点;
通知(Advice):在拦截到连接点后,所执行的操作;
前置通知
后置通知
例外通知
最终通知
环绕通知
切入点(Pointcut):匹配连接点的断言(通俗的讲,定义连接点的规则)
引入(引介,Introduction) :用来给一定类增加额外的属性或方法;
AOP 代理(AOP Proxy):由 AOP 框架所创建的代理对象;
织入(Weaving):将切面应用到程序或对象上,并且执行通知;
AOP :Aspect Oriented Program,面向切面(方面)的编程; 1、概念 切面(Aspect):是一个横切性的关注点的抽象,实质上与类是同一个级别的概念,只不过关注点不同;类是对象的抽象 连接点(Joinpoint):程序执行过程中某个特定的点,连接切面与程序执行的点; 通知(Advice):在拦截到连接点后,所执行的操作; 前置通知 后置通知 例外通知 最终通知 环绕通知 切入点(Pointcut):匹配连接点的断言(通俗的讲,定义连接点的规则) 引入(引介,Introduction) :用来给一定类增加额外的属性或方法; AOP 代理(AOP Proxy):由 AOP 框架所创建的代理对象; 织入(Weaving):将切面应用到程序或对象上,并且执行通知; 2、AOP 编程的基本步骤 1) 在 classpath 下,添加 spring aop 的依赖 jar 包 aopalliance.jar aspectjweaver.jar aspectjrt.jar 2) 在 spring 的配置文件中,增加 aop 相关的 schema <?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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 3) 在 spring 的配置文件中,启动对 aop 的支持 <!-- 配置切面 bean --> <bean id="logAspect" class="com.practice.logs.LogAspect" /> <!-- 切面的配置 --> <aop:config> <!-- 定义一个切面 --> <aop:aspect ref="logAspect"> <!-- 定义一个切入点 --> <aop:pointcut expression="execution(* com.practice.dao.*.*(..))" id="daoMethod"/> <!-- 定义前置通知 --> <aop:before method="doBefore" pointcut-ref="daoMethod"/> <!-- 定义最终通知 --> <aop:after method="doFinally" pointcut-ref="daoMethod"/> <!-- 定义例外通知 --> <aop:after-throwing method="doException" pointcut-ref="daoMethod"/> <!-- 定义后置通知 --> <aop:after-returning method="doAfter" pointcut-ref="daoMethod"/> <!-- 定义环绕通知 --> <aop:around method="doAround" pointcut-ref="daoMethod"/> </aop:aspect> </aop:config> 4) 编写切面 /** * @Aspect 用来注解这是一个切面 */ @Component @Aspect public class LogAspect { @Pointcut("execution(* com.practice.dao..*.add*(..))") private void anyMethod(){} //前置通知 @Before("anyMethod()") public void doBefore(){ System.out.println("执行方法之前"); } //后置通知 @AfterReturning(pointcut = "anyMethod()") public void doAfter(){ System.out.println("后置通知"); } //例外通知 @AfterThrowing(pointcut="anyMethod()") public void doException(){ System.out.println("例外通知"); } //最终通知 @After("anyMethod()") public void doFinally(){ System.out.println("最终通知"); } //环绕通知 @Around("anyMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知开始"); Object obj = pjp.proceed(); System.out.println("环绕通知结束"); return obj; } }
OOP(Object Oriented Programming)面向对象编程
OOP 引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合
OOP 允许开发者定义纵向的关系,但并不适合定义横向的关系
Spring MVC流程图
控制器类交给Spring管理
第一种方式是在SpringMVC 的配置文件中定义MyController 的bean 对象。
<bean class="com.host.app.web.controller.MyController"/>
第二种方式是在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
<context:component-scan base-package = "com.host.app.web.controller" > <context:exclude-filter type = "annotation" expression = "org.springframework.stereotype.Service" /> </context:component-scan >
使用Spring开发步骤
1) 搭建项目
2) 将 spring 的依赖 jar 包,添加到 classpath 目录下
spring_home/libs/xxx.jar
commons-logging-1.1.1.jar
3) 在 src 目录下,增加 spring 的配置文件,文件名自定义;(beans.xml , 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"> <!-- bean 标记用来配置一个对象 id 属性,指定对象的名字 class 属性,指定对象的类型 --> <bean id="userDao" class="com.practice.dao.UserDaoImpl" /> </beans>
4) 通过如下方式,在 spring 的容器中,取得所创建的对象
ApplicationContext ctx = new FileSystemXmlApplicationContext("src/beans.xml"); UserDao userDao = (UserDao) ctx.getBean("userDao"); userDao.add("admin");
bean 的配置详解
<bean id="userDao" class="com.practice.dao.UserDaoImpl" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"/>
bean 标记用来配置一个对象
id 属性,指定对象的名字
class 属性,指定对象的类型
scope 属性,用来指定创建 bean 的范围,取值如下:
singleton : 每次调用 getBean 都返回同一个实例(单例,默认值)【servlet】
prototype : 每次调用 getBean 都返回一个新的实例(原型)【struts】
request : 每次请求返回一个新的实例(仅在 web 应用中有效)
session : 每次会话返回一个新的实例 (仅在 web 应用中有效)
global session :与 session 的范围一致(仅支持 spring mvc)
lazy-init 属性,指定是否延迟创建 bean 的实例;
true :当首次调用 getBean 时,创建实例;
false :当初始化 spring 容器时,就创建实例;(默认值)
注:lazy-init 属性,仅对 singleton 范围内的 bean 有效,其它范围内的 bean总是延迟初始化的;
init-method 属性: 指定创建完成 bean 实例后,要执行的初始化方法,属性值就是方法名;
destroy-method 属性: 指定当销毁 spring 容器时,要执行的销毁方法,属性值就是方法名;
注:lazy-init 、init-method、destroy-method 属性,可以通过在 beans 标记中,设置默认值来实现;
Spring bean 的生命周期
注入依赖对象
基于 setter 方法注入
<bean id="userService" class="com.practice.service.UserServiceImpl"> <!-- property 用来配置依赖属性 name 指定需要注入的属性名 ref 指定使用那一个 bean 对象来注入,对应 bean 的 id 属性 --> <property name="userDao" ref="userDao" /> </bean>
类部分代码
private UserDao userDao; public void setUserDao(UserDao userDao) { System.out.println("-------" + userDao); this.userDao = userDao; }
基于构造器的注入
<bean id="userService" class="com.practice.service.UserServiceImpl"> <!-- constructor-arg 用来配置基于构造器的注入 index 属性,指定构造参数的索引,从 0 开始 ref 属性,指定用那一个 bean 对象来注入,对应 bean 的 id 属性 --> <constructor-arg index="0" ref="userDao" /> </bean>
在类中,需要提供相应的构造器
private UserDao userDao; public UserServiceImpl(UserDao userDao) { this.userDao = userDao; }
自动注入
<!-- autowire 指定自动注入的方式 byType : 通过方法的参数确定用那一个 setter 方法注入; byName :通过方法的名称,确定用那一个 setter 方法注入; constructor : 通过构造器注入 --> <bean id="userService" class="cn.practice.service.UserServiceImpl" autowire="constructor" />
Tips: 如果使用的是自动注入,则通过 xml 的配置,不能够看出对象之间的依赖关系,降低代码的可读性,所以一般不建议使用;
<bean id="pj" class="cn.practice.dao.PropInject"> <property name="strVal" value="strvalue"/> <property name="intVal" value="20" /> <property name="strArr"> <list> <value>strArr01</value> <value>strArr02</value> <value>strArr03</value> </list> </property> <property name="lists"> <list> <value>list01</value> <value>list02</value> <value>list03</value> </list> </property> <property name="sets"> <set> <value>set01</value> <value>set02</value> </set> </property> <property name="maps"> <map> <entry key="key01" value="value01" /> <entry key="key02" value="value02" /> </map> </property> <property name="props"> <props> <prop key="prop01">propvalue1</prop> <prop key="prop02">propvalue2</prop> <prop key="prop03">propvalue3</prop> </props> </property> </bean>
传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事务
Tips:缺点 1、如果所有的内容都配置在.xml文件中,会导致.xml文件过大;如果按需求分开.xml文件,又会导致.xml文件过多。总之这会使得配置文件的可读性与可维护性变得很低。 2、开发中,在.java文件和.xml文件之间不断切换,是一件麻烦的事。同时这种思维上的不连贯也会降低开发的效率。
Annotation(注解)
格式
// 注解格式 @注解名
引入
JDK1.5及以后版本引入,可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。
不会直接影响到程序的语义,只是作为注解(标识)存在,可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问
作用
用了注解之后,就不需要在xml文件中配置Bean了 ,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。
分类
注解参数的个数可以将注解分为:标记注解、单值注解、完整注解 三类。
Spring中的注解主要分为:类级别的注解、类内部的注解 两类。
类级别的注解: 如@Component、@Repository、@Controller、@Service以及JavaEE6的@ManagedBean和@Named注解,都是添加在类上面的类级别注解。
类内部的注解: 如@Bean、@Autowire、@Value、@Resource以及EJB和WebService相关的注解等,都是添加在类内部的字段或者方法上的类内部注解。
基于注解方式的配置
1、在 xml 中,增加 contenxt 命名空间
2、在 xml 中,开启动 spring 对注解配置的支持
3、在 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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启 spring 对注解的支持 --> <context:annotation-config /> <!-- 配置扫描那个包及其子包下的类,在扫描的过程中,如果类上面有如下注解,将会被自动纳入到 spring 的容器来管理 @Repository :专门用来注解 dao层 的对象 @Service :专门用注解 service 层的对象 @Controller : 专门用来注解 controller 对象 @Component : 当某个类的分工不是很明确,或者不是上面的三类,则使用此注解 --> <context:component-scan base-package="cn.practice" /> </beans>
4、在需要 Spring 容器管理的类上,增加相应的注解
@Repository("userDao") // 相当于<bean id="userDao" class="" /> @Scope("singleton") // 通过 @Scope 注解,注解 bean 的范围 public class UserDaoImpl implements UserDao { ... }
5、使用 @Resource 注解依赖对象
@Resource(name="userDao") public void setUserDao(UserDao userDao) { this.userDao = userDao; }
什么是Spring https://www.bilibili.com/read/cv9300303/
什么是Spring 注解 https://blog.csdn.net/wokewoke/article/details/104774417
Spring 注解 https://blog.csdn.net/weixin_42626412/article/details/124206162
如果万事开头难 那请结局一定圆满 @ Phoenixy
-------------------------------------------------------------------------------------