spring知识点总结
spring简介
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,Spring的核心是控制反转(IoC)和面向切面(AOP)
(核心一)IOC控制反转
控制反转就是将控制权从程序员手中转到了用户手中,降低程序耦合度,简单来说就是一个程序里声明另一个类作为属性,这个属性就是固定死了,IOC就数将每个联合的类交个spring来管理(个人理解),也就是对象的创建交给第三方。
DI依赖注入
DI依赖注入是实现IOC的一种方法,因为实现的好,普遍认为DI就是IOC。
- IOC实现方法
- xml:下面讲的是xml形式的,通过ClassPathXMLApplicationContext(“xml文件”)获得上下文。
- javaconfig:通过AnnotationConfigApplicationContext(java类对象)获得上下文。
(核心二)AOP面向切面编程
AOP为(Aspect Oriented Programming)的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容。学过安卓开发的同学就知道使用ASM插件也是AOP思想的实现方式之一。
首先先要了解设计模式——代理模式
-
代理模式
-
静态代理
使用代理类操作,不直接接触功能类,但是同样实现同一个接口。
-
动态代理
利用java反射机制减少代码量。
-
正文
初始spring基础——创建spring项目步骤(IOC)
-
使用maven创建spring项目
首先用maven导入依赖,不清楚maven可以先自行了解maven知识。
<!--pom.xml--> <!--这里只是添加其中一个依赖的建议,具体可以看spring官网,下载来的依赖一般放在默认的.m2下的repository文件夹里面--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> </dependencies>
-
创建项目
-
在java包里面的pojo(自己创建)文件夹创建实体类User,给一个name属性。
-
在resource里面创建applicationContext.xml(名字随便取,这是官方推荐名,我下文叫它beans.xml文件)文件配置beans(spring是一个容器,里面的东西叫做bean)。
<!--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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="User" class="com.qjl.demo.pojo.User"> <property name="name" value="QJL"/> </bean> </beans> <!--上面除了value还可以是ref,意为配置好的对象,一般用于在该文件配置好的非基本数据类型的对象。-->
-
构造器选择
<!--有参构造器三种使用方法如下,index是参数的顺序,ref找的是注册过的bean的ID--> <bean id="User" class="com.qjl.demo.pojo.User"> <constructor-arg index="0" value="Good"/> <constructor-arg index="1" ref="OtherBean"/> </bean> <!--使用类型匹配,必须参数类型都互不相同的情况下使用--> <bean id="User" class"com.qjl.demo.pojo.User"> <constructor-arg type="String" value="QJL"/> </bean> <!--推荐以下方法直接使用name指定参数名称--> <bean id="User" class="com.qjl.demo.pojo.User"> <constructor-arg name="name" value="QJL"/> </bean>
-
到这里注意下
注意点:想要成功配置一个bean,而该bean中有属性,则一定要设置setter方法spring容器就是靠setter方法来注入的。
注意点:IDEA默认写XML时会有提示,所以要是没有提示就是没有配置上下文,在项目结构里面找到模块,找到自己的模块展开,找到spring,再在最右边的区域添加上下文(左上角有个加号的)。
-
实例化xml文件,获取上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //这个对象可以将xml文件实例化,另外参数可以传入多个xml文件,同时这个这一步会将所 有注册的类new出来,调用的是无参构造器。 User user = (User)context.getBean("User"); //获得Object对象后转化一下就可以获得User对象,bean里面的参数传上面设置的bean的Id值,这一个方法始终只能获得一个示例。
-
如果有报log4j的错在resource文件夹里面创建log4j.properties文件并复制以下代码进去。
log4j.rootLogger=DEBUG,stdout ### console ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[${projectName}] [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %n%m%n ###复制进去先再说,是用来生成log信息的配置,以后用到再研究###
-
beans文件其他配置
-
alias别名
<alias name="User" alias="OtherUser"/> <!--name需要已经注册过,getBean()的时候可以传别名,下面是另一种别名方法--> <bean id="User" class="com.qjl.demo.pojo.User" name="OtherUser"/> <!--name还可以取多个比如,name="OtherUser1,OtherUser2",也可以用空格分割-->
-
scope(bean作用域)
<bean id="User" class="com.qjl.demo.pojo.User" scope="singleton"/> <!--默认是singleton就是单例模式,该bean只会创建一个实例--> <!--采用prototype的话,每次引用都会创建新的实例或者每次getBean()取出来的实例都不一样-->
-
import多个配置合成一个,当然配置上下文可以直接合并到已有配置文件
<import resource="OtherXml.xml"/> <!--在beans标签里面,还有不要重名-->
-
-
其他复杂注入
<!--数组--> <property name="names"> <array> <value>小A</value> <value>小B</value> <value>小C</value> </array> </property> <!--list--> <property name="names"> <list> <value>小A</value> <value>小B</value> <value>小C</value> </list> </property> <!--map--> <property name="names"> <map> <entry key="name" value="QJL"/> <entry key-ref="name" value-ref="QJL(引用型)"/> </map> </property> <!--null--> <property name="names"> <null/> </property> <!--Properties--> <property name="names"> <props> <prop key="A">value1</prop> <prop key="B">value2</prop> <prop key="C">value3</prop> </props> </property>
-
spring知识点详解
-
bean的自动装配
spring在上下文中自动寻找,并自动给bean装配属性。
分为三种方式:
-
xml手动装配(上面已讲)
-
java代码中装配(注解方式,最常用)
准备工作:使用注解需要以下操作
beans配置约束:
xmlns:context="http://www.springframework.org/schema/context"
beans里面开启注解:
<context:annotation-config/>
建议复制以下代码,因为还有xsi:schemaLocation需要配置
<?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"> <context:annotation-config/> </beans>
注解的使用:
//假设User类依赖Car类,前面加上@Autowired后就自动注入了,前提是 public class User{ //required=false具体功能是xml文件没有注册赋值就会为空,有则赋值。不写默认是true,这样的话该属性不能为空,xml文件必须注册,否则报错。 @Autowired(required = false) @Qualifier("User") //有多个注册用@Qualifier指定 private Car car; //或者在setter方法上使用,二选一 @Autowired public void setCar(Car car){ this.car = car; } } //这里还可以用java提供的注解@Resource(name="User")来代替@Autowired
在属性上使用@Autowired是使用反射实现,不调用setter方法
在setter方法上使用@Autowired直接调用setter方法注入
多个注册找不到使用@Qualifier指定
-
隐式自动装配(配置属性)
<bean id="User" class="com.qjl.demo.pojo.User" autowire="byName"/> <!--这样会根据User类中的属性自动在该xml文件寻找相同名称的已经注册的类注入,基本数据类型还是自己写property吧-->
<bean id="User" class="com.qjl.demo.pojo.User" autowire="byType"/> <!--这样会根据User类中的属性自动在该xml文件寻找相同类型的已经注册的类注入。-->
-
到这里注意:
注意点:byName找的是setter方法的名字,记得setter方法一定要和注册的bean的id对应(setName(),注册的bean的id就必须是name),这样方法名旁边就会有个绿色的叶子,说明可以成功注入。
注意点:byType要保证注册的id唯一不然会报错。
-
-
bean的注册(注解法)
-
添加约束(上面已讲)
-
启用注解
<!--这里介绍一种指定包开启注解的方式,需要配合@Component来使用--> <context:component-scan base-package="com.qjl.pojo"/> <!--在类需要注册的类前加上@Component,可以省去在xml里面配置bean的步骤--> <!--当然这句也要加上--> <context:annotation-config/>
@Component //注册 @Scope("prototype") //默认是”singleton单例模式“ public class User{ @Value("Qjl ") //简单的用value private String name; }
-
MVC架构的四个同功能注解(不懂请先了解MVC架构):
(dao接口)—>(service)—>(controller)—>(前端)
-
dao层(@Repository)
-
service层(@Service)
-
controller(@Controller)
上面三个注解功能和@Component注解功能一样都是将类交给spring管理,代替xml中配置bean。
<bean id="User" class="com.qjl.demo.pojo.User"/>
(用注解代替这个语句)。这三个注册的方法参考@Component,和它一样就行了。
注意这种方式一定要让context:component-scan扫到这个包,上面有配置
-
-
-
拓展知识(使用javaconfig代替xml文件管理bean)
//这是spring5之后逐渐开始推荐的一种方法,自行了解即可(beans.xml文件还是很多人用的)。 @Configuration @ComponentScan("com.qjl.demo") //类似扫描包 public class AppConfig{ @Bean public MyService myService{ return new MyServiceImpl(); } }
深入了解spring(AOP)
-
代理模式
-
静态代理(简单举例)
public interface AskForMoney{ void argue(); } public class Owner implements AskForMoney{ public void argue(){ System.out.println("讨债"); } } public class Company implements AskForMoney{ private Owner owner; public Company(){ owner = new Owner(); } public void argue(){ System.out.println("讨债公司上门"); owner.argue(); } } public class Test{ public static void main(String args[]){ Company company = new Company(); company.argue(); } } //这个例子就是使用了代理来执行,讨债公司替债主上门讨债。
-
动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
- java字节码实现:javasist(不需要了解虚拟机指令,不像asm)
-
-
JDK动态代理(拓展知识)
-
proxy类
-
InvocationHandler接口
//这里将一个接口实现类传给target 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); } @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ insert("AOP"+method.getName()); Object result = method.invoke(target,args); return result; } public void insert(String msg){ System.out.println("这就是"+msg); } } public class Demo{ public static void main(String args){ IUserImpl user = new IUserImpl(); ProxyInvocationHandler pi = new ProxyInvocationHandler(); pi.setTarget(user); IUser iu = (IUser)pi.getProxy(); iu.func(); } } //这里使用了java的放射实现了AOP,其中IUser是一个接口,IUserImpl实现了接口,通过代理调用接口的func()的同时,前面也调用了insert("AOP")方法,这样无论调用什么IUser的什么方法,都会执行invoke里面的设置的方法,无形中一次性给IUser所有的方法都插了一段代码,这就是所谓的AOP,面向切面,类后期被切开,自己放功能进去。
-
-
spring里的API实现AOP
-
切入点
- 方法前:MethodBeforeAdvice
- return后:AfterReturnAdvice
- 方法前后:MethodInterceptor
- 异常时:ThrowsAdvice
//注意是这个包org.springframework.aop.MethodBeforeAdvice; public class Before implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass(). getName()+"的"+method.getName()+"被执行了"); } } //这就是给方法前面添加Method其他自己类比。
<aop:config> <aop:pointcut id="pointcut" expression= "execution(String com.qjl.demo.pojo.*.*(..)))"/> <aop:advisor advice-ref="after" pointcut-ref="pointcut"/> <aop:advisor advice-ref="before" pointcut-ref="pointcut"/> </aop:config> <!--pointcut指定切入点,after是上面注册的bean,自己注册一下,execution表达式下面讲解-->
execution语句:
String com.qjl.demo.User.scream(…)
表示为com.qjl.demo.User下的返回值为String的scream方法注入环绕。其中…表示参数个数不定,类型不定。
* com.qjl.demo.*.*(String,int)
表示为com.qjl.demo下的所有参数是(String str,int age)的方法注入环绕,其中*表示通配。
execution(* *(…))
为项目所有类的所有方法注入环绕
-
-
使用自定义切面
//切面类提供切面方法 public class aspectj { public void before(){ System.out.println("before"); } public void after(){ System.out.println("after"); } }
<!--第5行是注册的切面类的id,所有要用的都要注册,这里不写了--> <!--第6行指定切入点--> <!--第7、8行指定第5行注册的切面类的哪个方法切入切入点,切入的位置由aop:before这样的标签决定--> <aop:config> <aop:aspect ref="aspectj"> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <aop:before method="before" pointcut-ref="pointcut"/> <aop:after method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config>
-
使用注解实现AOP
-
依赖下载
<!--maven文件依赖--> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
-
AOP配置
<!--头文件约束引入--> xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation= "http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
-
开启注解配置
<aop:aspectj-autoproxy/> <!--别忘了注册类,在beans.xml文件配置bean-->
-
标注一个切面
@Aspect public class AnnoAspect{ @Before("execution(* *(..))") public void before(){ System.out.println("before"); } @After("execution(* *(..))") public void after(){ System.out.println("after"); } @Around("execution(* *(..))") //可以只使用上面两个方法。 public void around(ProceedingJoinPoint point) throws Throwable { System.out.println(point.getSignature()); Object proceed = point.proceed(); System.out.println(point.getSignature()); } } //若三个方法一起使用,则point.proceed()是执行自己的方法的开始,以}大括号为结尾。 //所以执行顺序是,13行->4行—>自己的方法->15行->9行
-
-
声明式事务(使用AOP实现)
-
ACID原则
原子性、一致性、隔离性、持久性。
-
配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--AOP植入的事务管理,指定事务管理器,指定为哪种方法开启事务(示例)--> <tx:advice id="tx" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add"/> <tx:method name="*"/> </tx:attributes> </tx:annotation-driven>
植入事务
<aop:config> <aop:pointcut id="pointCut" expression="execution(* com.qjl.dao.*.*(..))"/> <aop:advisor advice-ref="tx" pointcut-ref="pointCut"/> </aop:config>
-
完结撒花
给学过spring的同学复习用的哈,大家自己统筹兼顾