Spring 基础知识学习

IoC 容器

Bean的使用

示例代码


<bean id="myId" class="MyClass">bean>
 // 初始化容器 ApplicationContext context = new
ClassPathXmlApplicationContext("application-context.xml");
// 获取对象
MyClass my = context.getBean("myId", MyClass.class);

Bean 的作用域

Bean 的作用域有 singleton, prototype, request, session, global session, application. 其中后四种是在web应用中使用的。

singleton: bean 的默认作用域, 也可通过 来显式地指定作用域。 保证只会有一个实例存在(后续创建的实例与第一个创建的实例指向同一个对象)_
prototype: 每次引用该bean时都会创建一个新的实例。

Bean实例创建、销毁时对资源的申请和释放

创建 在添加Bean时指定初始化方法的名称,并在类中实现该方法即可

<bean id="myId" class="MyClass" init-method="init">bean>
public class MyClass {
  public void init() {}
}

销毁 在添加Bean时指定销毁方法的名称,并在类中实现该方法即可

<bean id="myId" class="MyClass" init-method="cleanup">bean>
public class MyClass {
  public void cleanup() {}
}

注意: scope为singleton的bean的destroy方法则是在容器关闭时执行,而scope为prototype的bean是不会执行destroy方法的

依赖注入

依赖注入的方法

  • 构造函数依赖注入。为需要注入的类增加一个带参数的构造函数来完成依赖注入
public class MyClass {
  Dependency dp = null;
  public MyClass (Dependency instanceOfSubClassOfDependency) {
    dp = instanceOfSubDependency;
  }
}
  • Setter 方法依赖注入。
public class MyClass {
  Dependency dp = null;
  public void setDp(Dependency instanceOfSubClassOfDependency) {
    dp = instanceOfSubClassOfDependency;
  }
}

如果是强依赖,最好选择构造函数注入,如果是可选依赖,最好 选择 Setter 方法注入

示例代码

  • 通过构造函数传入基本类型来实现依赖注入

<bean id="myId" class="MyClass">
  
  <constructor-arg value = "...">constructor-arg>
  
  <constructor-arg index = "0" value = "...">constructor-arg>
  
  <constructor-arg type = "java.lang.String" value = "...">constructor-arg>
  
  <constructor-arg name = "argName" value = "...">constructor-arg>
bean>
  • 通过构造函数传入集合来实现依赖注入
public class MyClass {
  String arg0;
  int arg1;
  public MyClass (Map paras) {
    this.arg0 = paras.get("arg0");
    this.arg1 = Integer.valueOf(paras.get("arg1"));
  }
}

<bean id="myId" class="MyClass">
  <constructor-arg>
    <map>
      <entry key = "arg0" value = "value0">entry>
      <entry key = "arg1" value = "value1">entry>
    map>
  constructor-arg>
bean>
  • 通过配置文件来实现依赖注入

<bean id="myClassProperties" class =
  "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="classpath:myClass.properties"/> bean>
  <bean id="myId" class="MyClass">
    <constructor-arg name = "arg0" value = "${arg0}">constructor-arg>
    <constructor-arg name = "arg1" value = "${arg1}">constructor-arg>
  bean>
  • 注入自定义类
<bean id="myId2" class="MyClass2">
  <constructor-arg>
    <ref bean="myId"/>
  constructor-arg>
bean>
<bean id="myId" class="MyClass">
  <constructor-arg name = "arg0" value = "${arg0}">constructor-arg>
  <constructor-arg name = "arg1" value = "${arg1}">constructor-arg>
bean>
  • 通过 setter 函数来注入
public class MyClass {
  private String arg0;
  private int arg1;
  public void setArg0(String arg0) {
     this.arg0 = arg0;
  }
  public void setArg1(int arg1) {
    this.arg1 = arg1;
  }
}
<bean id="myId" class="MyClass">
  <property name="arg0" value="value0">property>
  <property name="arg1" value="value1">property>
bean>

自动装配与Annotation

自动装配

如果一个bean的依赖很多,或者需要添加依赖,手动修改配置文件会很低效。所以Spring提供了自动装配(Autowiring)的机制 自动装配有几种类型:
注意: Autowire是根据 setter 进行自动注入的,所以需要实现 setter方法

  • byName: 根据Bean名称进行装配
    xml

  • byType: 根据Bean类型进行装配
  • constructor: 构造函数,根据类型

Annotation

XML文件配置Bean与Annotation的比较:
* XML: 繁琐,代码独立
* Annotation: 简洁,代码耦合
几种Annotation:
1. @Component: 定义Bean
2. @Value: properties 注入
3. @Autowired & @Resource: 自动装配依赖
4. @PostConstruct & @PreDestroy: 生命周期回调

要使用Annotation 需要在 XML配置文件中添加

@Component("myId")
// 如果不指定id的话,默认为MyClass
public class MyClass {
  @Value("value0") String para0;
  @Value("value1") int para1;
}

Spring AOP

@AspectJ annotation-based AOP

使用时需要添加aspectj依赖 在spring的配置文件中也需要在beans的namespace中添加aop的tag

<beans xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
  <aop:aspectj-autoproxy />
  
  
  <bean id = "loggingAspect" class = "package.LoggingAspect"> bean>
beans>
// LoggingAspect.java
import org.aspectj.lang.annotation.Aspect;
// Aspect 的实现类需要添加 @Aspect的Annotation
@Aspect
public class LoggingAspect {
  // 定义Pointcut
  // Pointcut 表达式: designator(modifiers? return-type declaring-type? name(param) throws?) // 1.modifiers 是可选的,表示匹配的函数的修饰符,如public、private等 // 2.declaring-type 是可选的,表示匹配函数所在的包名,类名
  // 例1. 匹配所有的public函数
  // @Pointcut("execution(public * *(..))")
  // private void publicMethod() {}
  // 例2. 所有以save开头的函数
  // @Pointcut("execution(* save*(..))")
  // private void saveMethod() {}
  // 例3. 通过组合已有的Pointcut来实现匹配所有以save开头的public函数
  // @Pointcut("execution(publicMethod() && saveMethod())")
  // private void publicSaveMethod() {}
  @Pointcut("execution(* package.ObjectClass.*(..))")
  private void pointcutName() {}
  // 定义Advice
  @Before("package.LoggingAspect.pointcutName()") // 此处也可以直接用pointcut表达式代替而不是填写pointcut名称
  public void beforeActionName() {}
  // 其他Advice的annotation
  // @AfterReturing
  // @AfterThrowing
  // @After 等等
  // 通过JoinPoint 获取函数上下文信息
  @Before("pointcutName()")
  public void afterActionName(JoinPoint jp) {
    System.out.println(jp.getSignature());
  }
  // 注意@Around与其他有所不同,需要通过ProceedingJoinPoint获取上下文信息
  @Around("pointcutName()")
  public void aroundActionName(ProceedingJoinPoint pjp) {
    System.out.println("Start"); // 调用目标函数执行 pjp.proceed();
    System.out.println("Done");
  }
  // 获取返回值
  @AfterReturing(pointcut="pointcutName()", returning="retVal")
  public void afterReturingActionName(Object retVal) {
    // do something with return value
  }
}

Schema-based AOP



 <aop:config>
  <aop:aspect id="loggingAspect" ref="loggingBean">
    
    <aop:pointcut id="pointcutName" expression="...">
      
      <aop:before pointcut-ref="pointcutName" method="methodInLoggingAspect">aop:before>
    aop:pointcut>
  aop:aspect>
aop:config>

<bean id="loggingBean" class="package.LoggingAspect">
bean>

Spring 数据访问

Spring JDBC


<bean id="JdbcExampleDao" class="package.JdbcExmapleDao">
    <property name="dataSource" ref="dataSource"/>
bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${jdbc.dirverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
bean>
<context:property-placeholder location="db.properties"/>


jdbc.dirverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost/database
jdbc.username = username
jdbc.password = password

通过 Annotation 配置

@Repository
public class JdbcExampleDao {
    private JdbcTemplate jdbcTemplate;
    @Resource
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
}

Spring 事务管理

  • 统一的事务编程模型
  • 编程式事务及声明式事务(AOP)
// org.springframwork.transaction
// spring 提供的事务接口
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

TransactionDefinition

  • getName: 事务名称
  • getIsolationLevel: 隔离级别
  • getPropagationBehavior: 传播行为
  • getTimeout: 超时时间
  • isReadOnly: 是否只读事务

TransactionStatus

  • isNewTransaction: 是否是新的事务
  • hasSavepoint: 是否有savepoint
  • isCompleted
  • isRollbackOnly: 事务的结果是否是rollback-only
  • setRollbackOnly

隔离级别

  • ISOLATION_READ_UNCOMMITTED: 读未提交
  • ISOLATION_READ_COMMITTED: 读提交
  • ISOLATION_REPEATABLE_READ: 重复读
  • ISOLATION_SERIALIZABLE: 串行化
  • ISOLATION_DEFAULT: 默认

传播行为

  • PROPAGATION_MANATORY 必须在一个事务中运行,不存在事务则抛异常
  • PROPAGATION_NEVER 不应该在事务中运行,存在则抛异常
  • PROPAGATION_NOT_SURPPORTED 不应该在事务中运行,存在则挂起
  • PROPAGATION_SURPPORTS 不需要事务,有则在事务中运行
  • PROPAGATION_REQUIRED 默认的传播行为,必须在事务中执行,如果不存在, 则启动新事物 (内部事务会影响外部事物)
  • PROPAGATION_NESTED 必须在事务中执行,如果不存在,则启动新事物 (事务之间互不影响)
  • PROPAGATION_REQUIRES_NEW 必须在新事务中执行,挂起当前事务

代码示例

声明式事务


<beans xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
    bean>
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.dirverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    bean>
    <context:property-placeholder location="db.properties"/>
    
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        tx:attributes>
    tx:advice>
    
    <aop:config>
        <aop:pointcut id="daoOperation" expression="execution(Pointcut表达式)"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation"/>
    aop:config>
beans>
  • 通过注解的方式来声明事务
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
    <property name="driverClassName" value="${jdbc.dirverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
bean>
<context:property-placeholder location="db.properties"/>
@Transactionnal(propagation = Propogation.REQUIRED, rollbackFor = Exception.class)
public void doSomething() {
    // do work
}

@Transactional 的配置属性

  • value: 使用的TransactionManager
  • propagation
  • isolation
  • timeout
  • readOnly
  • rollbackFor
  • rollbackForClassName
  • noRollbackFor
  • noRollbackForClassName

编程式事务

  • TransactionTemplate
// 定义TransactionTemplate
public class Service {
    private final TransactionTemplate transactionTemplate;
    public Service(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
        this.transactionTemplate.setIsolationLevel(...);
        this.transactionTemplate.setTimeout(...);
        ...
    }

    // 使用TransactionTemplate
    public Object someMethod() {
        return transactionTemplete.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                // doSomething
                // return something
            }
        });
    }
}
  • PlatformTransactionManager 的实现
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("transactionName");
def.setPropagationBehavior(...);

TransactionStatus status = txManager.getTransaction(def);
try {
    // do some trasaction logic
} catch (Exception e) {
    txManager.roolback(status);
    throw ex;
}
txManager.commit(status);

整合MyBatis

添加 mybatis-spring 依赖

定义 SqlSessionFactoryBean

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
bean>

定义Mapper

public interface UserMapper {
    User getUser(String userId);
}
<mapper namespace="package.UserMapper">
    <select id = "getUser" resultType = "User">
        select * from users where id = #{userId}
    select>
mapper>

也可以用 Annotation 定义 Mapper

public interface UserMapper {
    @Select("select * from users where id = #{userId}")
    User getUser(@Param("userId") String userId);
}

定义 Mapper Bean

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface" value="package.UserMapper"/>
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>

也可通过Annotation配置



<mybatis:scan base-package="package"/>

使用 Mapper

public class SomeService {
    @Resource
    private UserMapper userMapper;

    public User getUser(String userId) {
        return userMapper.getUser(userId);
    }
}

通过 SqlSessionTemplate 可以使用 MyBatis 中 session 的接口

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory"/>
bean>
public class UserDAO {
    @Resource
    private SqlSession sqlSession;

    public User getUser(String userId) {
        return (User) sqlSession.selectOne("package.UserMapper.getUser", userId);
    }
}

Spring Web 框架

为什么要使用 Spring Web 框架?

  1. 需要一个框架来处理地址到处理逻辑的路由
  2. 需要一个框架来处理MVC

Web MVC 框架

Servlet 为核心构建的, 其核心是 DispatherServlet, 在其背后提供了一些工具来解决上述的问题。

  • Handler Mapping 用于解决请求路径到处理逻辑的映射关系
  • Controller 用于负责视图和模型之间的关系
  • View ResolverView 则是视图相关的工具

DispatherServlet 的使用

web.xml 文件中进行配置

<servlet>
  <servlet-name>exampleservlet-name>
  <servlet-class>org.springframework.web.servlet.DispatherServletservlet-class>
servlet>
<servlet-mapping>
  <servlet-name>exampleservlet-name>
  <url-pattern>/api/*url-pattern>
servlet-mapping>

同时在 WEB-INF 目录下配置一个名为 [servlet-name]-servlet.xml 的配置文件用于配置DispatherServlet

可配置项:

  • HandlerMapping
  • Controllers
  • View 解析相关

注意: DispatherServlet 不是唯一的,可以定义多个 DispatherServlet

可以通过 ContextLoaderListener 来配置多个 DispatherServlet 之间共同的配置

ContextLoaderListener

首先需要在 web.xml 文件中进行配置

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>

<context-param>
  
  <param-name>contextConfigLocationparam-name>
  <param-value>/WEB-INF/applicationContext*.xmlparam-value>
context-param>

实现 Controllers

// 需要在spring配置文件中开启自动发现才能使用Annotation来定义Controller
// 
@Controller
@RequestMapping(value = "/hello")
public class HelloController {
  // 当访问地址为 /hello/spring 时就会将请求转接到spring函数的处理中,输出"Hello, Spring Web"
  @RequestMapping(value = "/spring")
  public void spring(HttpServletResponse response) throws IOException {
    response.getWriter().write("Hello, Spring Web!!");
  }
}

RequestMapping 的属性

  • name: 名称
  • value & path : 路径, 比如 “/hello”
    注意:
    以下代码效果完全相同

    @RequestMapping("/hello")
    @RequestMapping(value = "/hello")
    @RequestMapping(path = "/hello")
  • method: 匹配请求的请求方法,如 “GET”
  • params: 请求参数,请求地址中加 “?key=value&key2=value2”. 当请求含有特定参数时才会转发到该请求处理函数
  • headers: 请求头
  • consumes: 请求的媒体类型,处理请求类型是特定数据类型的请求
  • produces: 响应的媒体类型,处理请求特定数据类型的请求

示例:

// RestAPI 的路径参数匹配
@RequestMapping(path="users/{userId}")
public String webMethod(@PathVariable String userId) {
  // do Work
}

函数参数:

  • HttpServletResponse/HttpServletRequest, HttpSession
  • Reader/Writer
  • @PathVariable
  • @RequestParam
  • @RequestHeader
  • HttpEntity
  • @RequestBody
  • Map/Model/ModelMap

函数返回值:

  • void 表示该请求处理函数不用返回view,函数自身会处理与view相关的逻辑
  • String: view 名称, 也可以用 @ResponseBody 这个 Annotation 来说明返回值不是view名称而是响应内容
  • HttpEntity
  • View: spring定义的view对象
  • Map: 代表返回的Model
  • Model
  • ModelAndView

示例代码

@RequestMapping(value = "/spring/{user}")
public void spring(
  @PathVariable("user") String user,
  @RequestParam("msg") String msg,
  @RequestHeader("host") String host,
  HttpServletRequest request,
  Writer writer) throws IOException {
    writer.write("URI: " + request.getRequestURI());
    writer.write("Hello, " + user + ": " + msg + ", host=" + host);
}
  • 表单
@RequestMapping(value = "/spring/login")
public void login(
  @RequestParam("name") String name,
  @RequestParam("password") String password,
  Writer writer)
    throws IOException {
      // do work
}

对于有很多信息的表单,挨个写 RequestParam 就会显得繁琐, Spring 提供了一个 ModelAttribute 的 Annotation
可以从参数匹配一个model

@RequestMapping(value = "/spring/login")
public void login(@ModelAttribute User user, Writer writer) {
  // do work
}
  • 上传文件
    首先定义一个与上传相关的bean
<bean id="multipartResolver"
  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <property name="maxUploadSize" value="100000"/>
bean>
@RequestMapping(path = "/form", method = RequestMethod.POST)
public String handleFormUpload(@RequestParam("file") MultipartFile file) {
  // do work
}
  • HttpEntity
@RequestMapping("/something")
public ResponseEntity handle(HttpEntity<byte[]> requestEntity) {
  String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");
  byte[] requestBody = requestEntity.getBody();
  // do something with request header and body
  HttpHeaders responseHeaders = new HttpHeaders();
  responseHeaders.set("MyResponseHeader", "MyValue");
  return new ResponseEntity("Hello World", responseHeaders, HttpStatus.CREATED);
}
  • @RequestBody & @ResponseBody
@RequestMapping(value = "/spring")
@ResponseBody
public String spring(@RequestBody String body) throws IOException {
  return "Hello" + body;
}

View 解析

  1. InternalResourceViewResolver
    一般处理 Servlet, JSP

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    
    <proerty name="prefix" value="/WEB-INF/jsp/"/>
    
    <proerty name="suffix" value=".jsp"/>
    bean>

    如果请求处理函数返回的view名称为 resultView, 则View的路径为 /WEB-INF/jsp/resultView.jsp

  2. FreeMarkerViewResolver
    针对 Freemarker 模板引擎的 viewResolver

    
    <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <proerty name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
    bean>
    
    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    
    <proerty name="cache" value="true"/>
    <proerty name="prefix" value=""/>
    <proerty name="suffix" value=".ftl"/>
    bean>
  3. ContentNegotiatingViewResolver
    是 ViewResolver 的组合,可以根据客户端的请求返回不同的view

    • 根据请求路径中的文件扩展名: 如: user.json, user.xml, user.pdf
    • 根据请求头中的Accept头来决定: 如 application/json, application/xml
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="viewResolvers">
      <list>
        <bean id="viewResolver" class="org.springframwork.web.servlet.view.freemarker.FreeMarkerViewResolver">
          <property name="cache" value="true"/>
          <property name="prefix" value=""/>
          <property name="suffix" value=".ftl"/>
        bean>
      list>
    property>
    
    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"/>
      list>
    property>
    bean>

Freemarker

Freemarker 指令

  • 开始标签: <#directivename parameters>
  • 结束标签:

if 指令:

<#if balance < 10> poor guy
<#elseif balance = 10> not so poor
<#else> haha
#if>

list 指令: freemarker 的 foreach

<#list animals as animal>
${animal.name}<br>
#list>

include 指令:
将需要重复使用的固定内容插入

<#include "/copyright_footer.html">

你可能感兴趣的:(Java,学习笔记,spring)