Spring框架的初步学习

时间:2021-09-14 14:45:47
(1) IOC 控制反转
所谓的控制反转就是应用本身不负责依赖对象的创建和维护,依赖对象的创建及维护是由
外部容器负责的(spring是外部容器之一)。这样控制权就由应用转移到了外部容器,控制权
的转移就是所谓的反转。
实例代码:
    //普通
    public class PersonServiceBean {
        private PersonDao personDao = new PersonDaoBean();
 
        public void save(Person person) {
            personDao.save(person);
        }
    }
    //IOC方式
   public class PersonServiceBean {
        private PersonDao personDao;
        //通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以用setter的方式进行注入
        public PersonServiceBean(PersonDao personDao) {
            this.personDao = personDao;
        }
        public void save(Person person) {
            personDao.save(person);
        }
    }
所谓依赖注入(Dependency Injection)就是指:在运行期间,由外部容器动态地将依赖对象注入到组件中。
(2)Spring的好处
  • 降低组件之间的耦合度;
  • 提供了很多服务,事务管理服务,spring core核心服务,持久化服务等等。事务管理方面,不需要我们手工控制事务,也需要手动的去处理事务的传播行为;
  • 容器提供单例模式支持,不需要再编写实现代码;
  • AOP技术,很容易实现权限拦截,运行期监控等功能;
  • 提供了很多的辅助类,如JdbcTemplate,HibernateTemplate
  • 提供了主流应用框架集成的支持,如hibernate,mybatis
(3)轻量级和重量级
主要看它使用了多少服务,使用的服务越多,容器要为普通java对象做的工作越多,必然会影响到应用的发布时间或者是运行性能;
(4)入门知识点
    实例化Spring容器
    ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"bean.xml"});
    Spring的配置文件可以指定多个,通过数组传入多个配置文件
spring是如何实例化bean的:
  1. 解析配置的xml文件,获取到beans节点下面的bean
  2. 将bean装入集合,对外提供getBean方法,通过反射技术,Class.forName('xxx').newInstance()方法来获取到bean对象的实例
/**
* 读取xml配置文件
* @param filename
*/
private void readXML(String filename) {
SAXReader saxReader = new SAXReader();
Document document=null;
try{
URL xmlpath = this.getClass().getClassLoader().getResource(filename);
document = saxReader.read(xmlpath);
Map<String,String> nsMap = new HashMap<String,String>();
nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
xsub.setNamespaceURIs(nsMap);//设置命名空间
List<Element> beans = xsub.selectNodes(document);//获取文档下所有bean节点
for(Element element: beans){
String id = element.attributeValue("id");//获取id属性值
String clazz = element.attributeValue("class"); //获取class属性值
BeanDefinition beanDefine = new BeanDefinition(id, clazz);
XPath propertySub = element.createXPath("ns:property");
propertySub.setNamespaceURIs(nsMap);
List<Element> propertiesElement = propertySub.selectNodes(element);
for (Element property : propertiesElement) {
String name = property.attributeValue("name");
String ref = property.attributeValue("ref");
String value = property.attributeValue("value");
// System.out.println(name + "==" + ref);
beanDefine.getProperties().add(new PropertyDefinition(name, ref,value));
}
beanDefines.add(beanDefine);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 完成bean的实例化
*/
private void instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))
sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
} }
是如何依赖注入bean的:
     3.   在每个bean对应的java对象中,有需要注入的bean的set方法,通过Introspector.getBeanInfo获取到被注入bean对象的属性值,与配置文件中配置的bean进行匹配,如果匹配上通过pDescriptor.getWriteMethod方法,反射的方式将匹配到的bean注入到属性中去。
/**
* 为bean对象的属性注入值
*/
private void injectObject() {
for(BeanDefinition beanDefinition : beanDefines) {
Object bean = sigletons.get(beanDefinition.getId());
try {
if(null != bean) {
//得到bean的属性集合(<bean id="personDao" class="com.test.dao.impl.PersonDaoImpl" />)
PropertyDescriptor[] pd = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();//包含class属性
for(PropertyDefinition propertyDefinition : beanDefinition.getProperties()) {//配置文件中用户定义的bean属性
for(PropertyDescriptor pDescriptor : pd) {
if(propertyDefinition.getName().equals(pDescriptor.getName())) {
Method setter = pDescriptor.getWriteMethod();//获取属性的setter方法
if(null != setter) {
Object value = null;
if(propertyDefinition.getRef() != null && !propertyDefinition.getRef().trim().equals("")) {
value = sigletons.get(propertyDefinition.getRef());
}else {
value = ConvertUtils.convert(propertyDefinition.getValues(), pDescriptor.getPropertyType());//注入基本类型的值
}
setter.setAccessible(true);//如果setter方法是private的话,invoke会报错,需要设置
setter.invoke(bean, value);//把引用对象注入到属性
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
实例化bean的方式有三种:
  1. 构造函数(用的最多)
  2. 静态工厂
  3. 实例工厂
bean的作用域:
  • singleton(默认):在spring ioc容器中一个bean定义只有一个实例对象,默认情况下会在容器启动的时候初始化,如果需要延迟初始化,需要在配置bean的时候加上lazy_init="true",这样只有在获取的时候才会初始化,对单个bean <bean id="person" class="com.test.bean.Person" lazy-init="true">,容器中所有bean都延迟加载<beans default-lazy-init="true">
  • prototype:每次从容器获取新的bean对象 <bean id="person" class="com.test.bean.Person" scope="prototype">,在getBean的时候进行实例化
bean的生命周期:
    默认情况下容器启动,AbstractApplicationContext中给出了一个close方法,
    其说明是Close this application context, destroying all beans
    AbstractApplicationContext ctx =  new ClassPathXmlApplicationContext(new String[]{"bean.xml"});
    ctx.close();
依赖注入手工装配或自动装配,自动装配会产生未知情况,不利于开发)
  • set属性的方式注入bean:(可以被多个bean同时使用)
      <bean id="personService" class="com.test.service.impl.PersonServiceImpl">
          <property name="personDao" ref="personDao"></property> 
                <!-- name是指要注入的属性,ref对应要注入的bean的名称,通过反射技术将bean设给了属性 -->
         </bean>
  • 内部bean的方式注入(只可以被这一个bean使用)
  • 使用构造器注入
          通过 constructor-arg 来注入
  • 使用Field注入(用于注解方式)
    1. 引入jar文件 common.annotations.jar
    2. 在xml添加配置如下
       命名空间  xmlns:context="http://www.springframework.org/schema/context"
       schema

          http://www.springframework.org/schema/context


  • 打开注解 <context:annotation-config />,这个配置隐式注册了多个对注释进行解析处理的处理器
  • 使用@Autowired或@Resource注解方式进行装配。区别在于:@Autowired默认按类型装配,
        @Resource默认按名称装配。建议使用Resource,这个是jdk提供的注解。
        自定义一个注解,该注解可用于set方法和字段上面,供处理器处理,将bean注入到字段或set方法中
package junit.test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface TydicResource {
/**
* Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:
* 1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源代码级别保留,编译时就会被忽略
* 2.RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
* 3.RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
*
* Target(注解的作用目标)
*/
String name() default ""; }
package com.test.service.impl;

import javax.annotation.Resource;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; import junit.test.TydicResource; import com.test.dao.PersonDao;
import com.test.service.PersonService; @Service("personService") @Scope("prototype")
public class PersonServiceImpl extends Object implements PersonService { // @TydicResource(name="xgw")<!-- 注解通过name寻找到bean注入 -->
@TydicResource
// @Resource
public PersonDao personDao; public PersonDao getPersonDao() {
return personDao;
}
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
@Override
public void add() {
personDao.add();
}
}

查看set方法和field上是否加了注解

/**
* 通过注解方式注入bean
* 仿 注解处理器代码
*/
private void annotationInjectObject() {
for(String beanName : sigletons.keySet()) {
Object bean = sigletons.get(beanName);
if(null != bean) {
try {
PropertyDescriptor[] pd = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();//定义的javabean的属性集合
for(PropertyDescriptor pDescriptor : pd) {
Method setter = pDescriptor.getWriteMethod(); //属性的set方法
if(null != setter && setter.isAnnotationPresent(TydicResource.class)) {
TydicResource resource = setter.getAnnotation(TydicResource.class);//是否存在这个注解
Object value = null;
if(resource.name()!=null && !"".equals(resource.name())) {//注解中标明name属性
value = sigletons.get(resource.name());
}else {
value = sigletons.get(pDescriptor.getName());
if(value == null) { //如果在属性中也没有找到,就按类型去寻找
for(String key : sigletons.keySet()) {
/**
* isAssignableFrom
* either the same as, or is a superclass or * superinterface of, the class or interface
* 这个属性的类型如果是该磊的接口或者父类或者就是该类的话,返回true
*/
if(pDescriptor.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())) {
value = sigletons.get(key);
break;
}
}
}
}
setter.setAccessible(true);
setter.invoke(bean, value);
}
} Field[] fields = bean.getClass().getFields();
for(Field field : fields) {
TydicResource resource = field.getAnnotation(TydicResource.class);//是否存在这个注解
Object value = null;
if(resource.name()!=null && !"".equals(resource.name())) {//注解中标明name属性
value = sigletons.get(resource.name());
}else {
value = sigletons.get(field.getName());
if(value == null) { //如果在属性中也没有找到,就按类型去寻找
for(String key : sigletons.keySet()) {
if(field.getType().isAssignableFrom(sigletons.get(key).getClass())) {
value = sigletons.get(key);
break;
}
}
}
}
field.setAccessible(true);
field.set(bean, value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
自动扫描
spring2.5以后引入组件自动扫描机智,可以在类路径下寻找标注了
@Component 泛指组件,可通用
@Service         用于标注业务层组件
@Controller    用于标注控制层组件(如action)
@Respostory   用于标注数据访问组件dao层
注解的类,纳入到spring容器中管理
在配置文件中添加 <context:component-scan base-package="com.test.*" />,base-package为需要被扫描的包路径,
有了 <context:component-scan base-package="com.test.*" />配置之后就可以将<context:annotation-config />去掉,
前者注册的处理器包含了后者的。
Spring框架的初步学习Spring框架的初步学习Spring框架的初步学习
由于spring初始化的时候采用的是单例,如果需要实现多个bean的话,可以加@Scope("prototype")来改变。
@PostConstruct   默认初始化方法
@PreDestroy
(4)AOP
使用Proxy创建代理对象的时候,目标对象必须实现一个接口,面向接口的时候才能使用Proxy创建代理对象。
 
joinpoint(连接点)
    被拦截的方法,需要被处理的可以被看做连接点(joinpoint),在spring中只支持方法类型的连接点,实际上连接点还可以是field或类构造器
pointcut(切入点)
    是指所有要被拦截的joinpoint定义
weave(织入):
    指将aspect应用到target对象并导致proxy对象创建的过程叫织入
Introdunction(引入):
    在不修改代码的情况下,在运行期动态的添加方法或field
aspect(切面):
    是指对横切性关注点的抽象,就像类是对实体的抽象一样。    
抽象过程的体现
package com.test.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import com.test.service.impl.PersonServiceImpl; public class JDKProxyFactory implements InvocationHandler {
private Object targetObject; public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
PersonServiceImpl personServiceImpl = (PersonServiceImpl) this.targetObject;
Object result = null;
if(personServiceImpl.getUser()!=null) {
result = method.invoke(targetObject, args);
} return result;
}
}
package com.test.aop;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import com.test.service.impl.PersonServiceImpl; public class CglibProxyFactory implements MethodInterceptor {
private Object targetObject;//代理的目标对象 public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();//该类用于生成代理
/**
* cglib创建的代理,是目标对象的子类,能够复制非final修饰的所有方法
*/
enhancer.setSuperclass(this.targetObject.getClass());//设置父类
enhancer.setCallback(this);//设置回调用对象本身
return enhancer.create();
} @Override
public Object intercept(Object proxy, Method method, Object[] aobj,
MethodProxy methodproxy) throws Throwable {
PersonServiceImpl personServiceImpl = new PersonServiceImpl();
Object result = null;
if(personServiceImpl.getUser() != null) {
result = methodproxy.invoke(targetObject, aobj);
}
return result;
} }
Spring AOP编程
如果Spring检测到类实现了接口,则会用jdk提供的Proxy动态代理技术进行拦截,如果类没有实现接口,则会用cglib的方式进行创建代理对象。
切面:@Aspect
声明一个切入点:@Pointcut("executor(* com.test.service..*.*(..))")
各种通知:@Before,@AfterReturning,@After,@AfterThrowing
@Around     需要加上切入点("anyMethod()"),才可以。
首先默认会拦截切点后面所有指定的方法
Spring框架的初步学习
同时可以再添加限制条件,进行拦截限制
  
(5)事务管理
引入命名空间:xmlns:tx="http://www.springframework.org/schema/tx
//配置数据源
  • 直接配置在spring的配置文件中
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
   <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
   <property name="username" value="root"></property>
   <property name="password" value="root"></property>
  </bean>
  • 配置在配置文件(db.properties)中
采用第一种方式即可,配置文件方式了解即可
  //配置事务管理器
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="dataSource"></property>
  </bean>
//打开事务管理
<tx:annotation-driven transaction-manager="txManager" />
 
运行期(RuntimeException unchecked)异常会进行回滚
可以通过设置,让RuntimeException异常不回滚
Spring框架的初步学习
可以通过设置,让checked的异常回滚
Spring框架的初步学习
 
由于事务会影响性能,所以如果不需要进行事务管理的话,就要声明该方法不需要事务管理,比如说查询列表或者查情详情,事务会被挂起,在该方法调用结束后,原先的事务会恢复执行。@Transactional(propagation=Propagation.NOT_SUPPORTED)
 
XML方式配置事务
Spring框架的初步学习
建议使用注解方式,但是在实际项目中运用大多是xml方式。
 
数据库事务隔离级别
Spring框架的初步学习
 

Spring框架的初步学习的更多相关文章

  1. Spring框架-AOP详细学习&lbrack;转载&rsqb;

    参考博客:https://blog.csdn.net/qq_22583741/article/details/79589910#4-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85% ...

  2. Spring框架零基础学习(一):IOC&vert;DI、AOP

    文章目录 一.IDEA创建Spring项目 二.Spring: IOC和DI 三.Spring: AOP 参考链接: HOW2J.CN:Spring idea创建一个spring项目 一.IDEA创建 ...

  3. 学习Spring框架等技术的方向、方法和动机

    学习Spring框架最早学习Spring框架是在大二的时候,当时看了几本书,看了一些视频,主要是传智播客的.更多的,还是写代码,单独写Spring的,也有与Struts和Hibernate等框架整合的 ...

  4. Spring框架学习一

    Spring框架学习,转自http://blog.csdn.net/lishuangzhe7047/article/details/20740209 Spring框架学习(一) 1.什么是Spring ...

  5. 老周的ABP框架系列教程 -》 一、框架理论初步学习

    老周的ABP框架系列教程 -- 一.框架理论初步学习   1. ABP框架的来源与作用简介 1.1  简介 1.1.1       ABP框架全称为"ASP.NET Boilerplate ...

  6. Spring框架学习之第2节

    传统的方法和使用spring的方法 使用spring,没有new对象,我们把创建对象的任务交给了spring的框架,通过配置用时get一下就行. 项目结构 applicationContext.xml ...

  7. Spring框架学习 - 配置

    [资料] ★★☆ Spring 中提供一些Aware相关接口,像是BeanFactoryAware. ApplicationContextAware.ResourceLoaderAware.Servl ...

  8. 深入浅出学习Spring框架(四):IoC和AOP的应用——事务配置

    在前文 深入浅出学习Spring框架(一):通过Demo阐述IoC和DI的优势所在. 深入浅出学习Spring框架(三):AOP 详解 分别介绍了Spring的核心功能——IoC和AOP,光讲知识远远 ...

  9. spring框架学习(三)junit单元测试

    spring框架学习(三)junit单元测试 单元测试不是头一次听说了,但只是听说从来没有用过.一个模块怎么测试呢,是不是得专门为一单元写一个测试程序,然后将测试单元代码拿过来测试? 我是这么想的.学 ...

随机推荐

  1. 关于favicon&period;ico,shortcut icon,icon

    引入一篇文章.关于favicon.ico二三事. http://www.cnblogs.com/LoveJenny/archive/2012/05/22/2512683.html 一直对favicon ...

  2. appium &plus; python 环境搭建

    所需:JDK.Android SDK.Appium服务程序.Appium客户端程序 1. 安装最新的JDK,并配置环境变量. JAVA_HOME=C:\Program Files (x86)\Java ...

  3. 3&period;SRS文档

    1.功能需求 本程序的使用者为局域网用户.程序实现的主要功能是局域网的常见格式的文件的传 输.其用例图如图1.本程序可通过可视化操作界面实现一对多的文件传输. 1.1模块分析 为实现局域网文件传输, ...

  4. Contains Duplicate II 解答

    Question Given an array of integers and an integer k, find out whether there are two distinct indice ...

  5. 在VisualStudio中显示当前的分支名

    当项目多的时候,当分支多的时候,当会议多的时候. 你打开VS,你是否犹豫过,"我现在是打开的哪个分支?!!!!??!" 如果你米有犹豫过,是否有过,"FXXXXK, 我怎 ...

  6. iOS-桥接方式

    很多时候都会使用到CoreFoundation的函数,其返回值为CoreFoundation框架的对象,如果想转换为Foundation框架的对象就可以使用桥接方式来搞定. 示例代码: CFStrin ...

  7. 201521123119《Java程序设计》第7周学习总结

    1. 本周学习总结 2. 书面作业 Q1.ArrayList代码分析 Q1.1 解释ArrayList的contains源代码 这段代码的主要目的是判断在对ArrayList遍历时所用的方法,在输入参 ...

  8. ReentrantLock 实现原理

    使用 synchronize 来做同步处理时,锁的获取和释放都是隐式的,实现的原理是通过编译后加上不同的机器指令来实现. 而 ReentrantLock 就是一个普通的类,它是基于 AQS(Abstr ...

  9. 【Java每日一题】20170117

    20170116问题解析请点击今日问题下方的“[Java每日一题]20170117”查看(问题解析在公众号首发,公众号ID:weknow619) package Jan2017; import jav ...

  10. layui table动态表头 改变表格头部 重新加载表格

    改变头部原理: 删除原来表格, 重新建立DOM元素, 重新加载table,实现表头改变 明白了原理, 我相信大家都能写出来了, table.reload(ID, options)目前好像还不支持con ...