学习笔记 --Spring 框架

本贴最后更新于 2528 天前,其中的信息可能已经事过景迁

//概述

轻量级,一站式,开发框架

IoC,Inversion of Control,控制反转

DI,Dependency Injection,依赖注入

AOP,Aspect-Oriented Programming,面向切面编程:业务逻辑与非业务逻辑分离,如日志、安全...

IoC 容器:

对象创建、装配

对象生命周期管理

上下文环境

//IoC 容器

IoC = ApplicationContext (org.springframework.context, spring-context)

初始化

ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");

ApplicationContext context = new FileSystemXmlApplicationContext("/home/user/conf/application-context.xml");

或在 web.xml 中

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:application-context.xml</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Bean 定义

<?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:p="http://www.springframework.org/schema/p"
	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">

	<bean id="screwDriver" class="com.netease.course.ScrewDriver"></bean>

</beans>

Bean 使用

//初始化容器

ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");

//获取对象

ScrewDriver screwDriver = context.getBean("screwDriver", ScrewDriver.class);

//使用对象

screwDriver.use();

Bean 作用域

singleton,单例

<bean id="screwDriver" class="com.netease.course.ScrewDriver" scope="singleton"></bean>

默认为单例
prototype,每次引用创建一个实例

<bean id="screwDriver" class="com.netease.course.ScrewDriver" scope="prototype"></bean>

request scope, requestBean
session scope, sessionBean
application scope, appBean
global scope

Bean 生命周期回调

创建,申请资源
可以通过实现接口

public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}

或者直接在 application-context.xml 中配置

<bean id="screwDriver" class="com.netease.course.ScrewDriver" init-method="init"></bean>

对应代码

public class ScrewDriver {
   public void init() {
		System.out.println("Init screwDriver");
   }
}

销毁
可以通过实现接口

public interface DisposableBean {
	void destroy() throws Exception;
}

或者直接在 application-context.xml 中配置

<bean id="screwDriver" class="com.netease.course.ScrewDriver" destroy-method="cleanup"></bean>

对应代码

public class ScrewDriver {
   public void cleanup() {
		System.out.println("Cleanup screwDriver");
   }
}

关闭 Bean

((ConfigurableApplicationContext) context).close();

依赖注入

构造函数,强依赖

Setter 函数,可选依赖

配置 bean 的类的构造函数的参数

 <bean id="straightHeader" class="com.netease.course.StraightHeader">
	  <constructor-arg value="red"></constructor-arg>
	  <constructor-arg value="15"></constructor-arg>
  </bean>

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg index="0" value="red"></constructor-arg>
    <constructor-arg index="1" value="15"></constructor-arg>
</bean>

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg type="java.lang.String" value="red"></constructor-arg>
    <constructor-arg type="int" value="15"></constructor-arg>
</bean>

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg name="color" value="red"></constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

需要传递集合类型的构造函数参数(如 map)时

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg>
        <map>
            <entry key="color" value="red"></entry>
            <entry key="size" value="15"></entry>
        </map>
    </constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

传入 list 时

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg>
        <list>
            <value>red</value>
            <value>15</value>
        </list>
    </constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

传入 Properties 时

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <constructor-arg>
        <props>
            <prop key="color">red</prop>
            <prop key="size">15</prop>
        </props>
    </constructor-arg>
    <constructor-arg name="size" value="15"></constructor-arg>
</bean>

从外部倒入配置时

  <bean id="straightHeader" class="com.netease.course.StraightHeader">
	  <constructor-arg name="color" value="${color}"></constructor-arg>
	  <constructor-arg name="size" value="${size}"></constructor-arg>
  </bean>
  <bean id="headerProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location" value="classpath:header.properties" />
</bean>

在一个 bean 中添加所依赖的 bean

<bean id="screwDriver" class="com.netease.course.ScrewDriver">
    <constructor-arg>
        <ref bean="straightHeader" />
    </constructor-arg>
</bean>

通过 Setter 方法注入依赖

<bean id="straightHeader" class="com.netease.course.StraightHeader">
    <property name="color" value="${color}"></property>
    <property name="size" value="${size}"></property>
</bean>

自动装配

constructor 是按 byType 方式注入

   <bean id="screwDriver" class="com.netease.course.ScrewDriver" autowire="constructor">
	</bean>

   <bean id="screwDriver" class="com.netease.course.ScrewDriver" autowire="byName">
	</bean>

 <bean id="screwDriver" class="com.netease.course.ScrewDriver" autowire="byType">
  </bean>

Annotation

@Component:定义 Bean,或 @Component("name")

@Value:properties 注入

@Autowired & @Resource:自动装配依赖

@PostConstruct & @PreDestroy:生命周期回调

在 xml 中加入

 <context:component-scan base-package="com.netease.course" />

//AOP 技术

AOP 术语

Aspect:日志、安全等功能

Join point:函数执行或者属性访问

Advice:在某个函数执行点上要执行的切面功能

Pointcut:匹配横切目标函数的表达式

Advice 类型

Before:函数执行之前

After returning:函数正常返回之后

After throwing:函数抛出异常之后

After finally:函数返回之后

Around:函数执行前后

Spring AOP

非完整 AOP 实现

整合 AOP 和与 IoC

XML schema-based AOP

@AspectJ annotation-based AOP

@AspecsJ annotation-based AOP

aspectjweaver.jar

<?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:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

	<aop:aspectj-autoproxy />

</beans>

定义 Aspect

   <bean id="loggingAspect" class="com.netease.course.LoggingAspect">
		   <!-- configure properties of aspect here as normal -->
   </bean>

相应代码中类名前加入 @Aspect

定义 Pointcut

@Pointcut("execution(* com.netease.course.Calculator.*(..))")

private void arithmetic() {}

Pointcut 表达式

designator(modifiers? return-type declaring-type? name(param) throws?)

designator: execution, within

modifiers: public, private

return-type: 返回类型,*

declaring-type: 包名,类名

name: 函数名,*

param: 参数列表,()无参,(..)任意参数

throws: 异常类型

可以组合

定义 Advice

@Before("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

  //

}

@Before("execution(* com.netease.course.Calculator.*(..))")

public void doLog() {

    //

}

@AfterReturning("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

    //

}

@AfterThrowing("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

    //

}

@After("com.netease.course.LoggingAspect.arithmetic()")

public void doLog() {

    //

}

Advice 参数

函数上下文信息

@Before("com.netease.course.LoggingAspect.arithmetic()")

public void doLog(JoinPoint jp) {

    System.out.println(jp.getSignature() + ", " + jp.getArgs());

}

@Around("com.netease.course.LoggingAspect.arithmetic()")

public void doLog(ProceedingJoinPoint pjp) {

    System.out.println("start method: " + pjp.toString());

    Object retVal = pjp.proceed();

    System.out.println("stop method: " + pjp.toString());

}

返回值

@AfterReturning(pointcut="com.netease.course.LoggingAspect.arithmetic()"),

                returning="retVal")

public void doLog(Object retVal) {

    //

}

异常

@AfterThrowing(pointcut="com.netease.course.LoggingAspect.arithmetic()"),

               throwing="ex")

public void doLog(IllegalArgumentException ex) {

    //

}

目标函数参数

@Before("com.netease.course.LoggingAspect.arithmetic() && args(a, ..)")

public void doLog(JoinPoint jp, int a) {

    //

}

XML schema-based AOP

定义 Aspect 和 PointCut

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:pointcut id="arithmetic" expression="execution(* com.netease.course.Calculator.*(..))" />
	 </aop:aspect>
 </aop:config>

定义 Advice

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:before pointcut-ref="arithmetic" method="doLog" />
	 </aop:aspect>
 </aop:config>

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:before pointcut="execution(* com.netease.course.Calculator.*(..))" method="doLog" />
	 </aop:aspect>
 </aop:config>

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:after-returning pointcut-ref="arithmetic" returning="retVal" method="doLog" />
	 </aop:aspect>
 </aop:config>

 <aop:config>
	 <aop:aspect id="loggingAspect" ref="loggingBean">
		 <aop:after-throwing pointcut-ref="arithmetic" throwing="ex" method="doLog" />
	 </aop:aspect>
 </aop:config>

   <aop:aspect id="loggingAspect" ref="loggingBean">
       <aop:around pointcut-ref="arithmetic" method="doLog" />
   </aop:aspect>

//数据访问

DAO,Data Access Object

数据访问相关接口

ORM,Object Relation Mapping

对象关系映射

DataSource (javax.sql)

DriverManagerDataSource (org.springframework.jdbc.datasource)

BasicDataSource (org.apache.commons.dbcp)

   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
	   <property name="driverClassName" value="${jdbc.driverClassName}" />
	   <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" />

JdbcTemplate (org.springframework.jdbc.core)
设置 JdbcTemplate 的数据库配置信息

private JdbcTemplate jdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

查询操作

    int rowCount = this.jdbcTemplate.queryForObject("select count(*) from user", Integer.class);
    int countOfNamedJoe = this.jdbcTemplate.queryForObject("select count(*) from user where first_name = ?", Integer.class, "Joe");
    String lastName = this.jdbcTemplate.queryForObject("select last_name from user where id = ?", new Object[]{1212L}, String.class);

更改操作

this.jdbcTemplate.update("insert into user (first_name, last_name) values (?, ?)", "Meimei", "Han");
this.jdbcTemplate.execute("create table user (id integer, first_name varchar(100), last_name varchar(100))");

对象匹配

    User user = this.jdbcTemplate.queryForObject("select last_name from user where id = ?",
            new Object[]{1212L},
            new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setFirstName(rs.getString("first_name"));
            user.setLastName(rs.getString("last_name"));
            return user;
        }
    });

    List<User> users = this.jdbcTemplate.query("select last_name from user where id = ?",
            new Object[]{1212L},
            new RowMapper<User>() {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setFirstName(rs.getString("first_name"));
            user.setLastName(rs.getString("last_name"));
            return user;
        }
    });

定义 JdbcTemplate

public class JdbcExampleDao implements ExampleDao {
	private JdbcTemplate jdbcTemplate;
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}
	//...DAO接口实现
}

可以在 xml 中配置相应的 bean
也可以用 annotation 的方法,如下:

@Repository
public class JdbcExampleDao implements ExampleDao {
	private JdbcTemplate jdbcTemplate;
	@Autowired
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}
	//...DAO接口实现
}

NamedParameterJdbcTemplate (org.springframework.jdbc.core)

private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource) {
    this.namedParameterJdbcTemplate= new NamedParameterJdbcTemplate (dataSource);
}

public int countOfUserByFirstName(String firstName) {
	String sql = "select count(*) from usertest where first_name = :first_name";
	Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
	return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}

其他 api 接口

queryForObject(String sql, Map<String, ?> paramMap, RowMapper<T> rowMapper)
queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType)

SqlParameterSource: MapSqlParameterSource, BeanPropertySqlParameterSource (org.springframework.jdbc.core.namedparam)
如:
public int countOfUserByFirstName(User user) {
	String sql = "select count(*) from usertest where first_name = :first_name";
	SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(user);
	return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}

异常处理

DataAccessException, "unchecked", 是一个基类 (org.springframework.dao)

//事务管理

spring 事务管理

统一的事务编程模型,编程式事务及声明式事务(AOP)

public interface PlatformTransactionManager {

	TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;

}

事务管理器

PlatformTransactionManager (org.springframework.transaction):

DataSourceTransactionManager (org.springframework.jdbc.datasource), JDBC

HibernateTransactionManager (org.springframework.orm.hibernate), Hibernate

TransactionDefinition

getName:事务名称

getIsolationLevel:隔离级别

getPropagationBehavior:传播行为

getTimeout:超时时间

isReadOnly:是否只读

TransactionStatus

isNewTransaction:是否是新事务

hasSavePoint:是否有 savepoint(诊断,NESTED)

isCompleted:是否完成

isRollbackOnly:事务结果是否是 rollback-only
setRollbackOnly:设置事务为 rollback-only

隔离级别

ISOLATION_READ_UNCOMMITTED:读未提交

ISOLATION_READ_COMMITTED:读提交

ISOLATION_REPEATABLE_READ:重复读

ISOLATION_SERIALIZABLE:串行化

ISOLATION_DEFAULT:默认

传播行为

PROPAGATION_MANDATORY:必须在一个事务中运行,不存在则抛异常

PROPAGATION_NEVER:不应该在事务中运行,存在则抛异常

PROPAGATION_NOT_SUPPORTED:不应该在事务中运行,存在则挂起

PROPAGATION_SUPPORTS:不需要事务,有则在事务中执行

PROPAGATION_REQUIRED:必须在事务中执行,如果不存在,则启动新事务(内部事务会影响外部事务)

PROPAGATION_NESTED:必须在事务中执行,如果不存在,则启动新事务(事务之间互不影响)

PROPAGATION_REQUIRES_NEW:必须在新事务中执行,挂起当前事务(独立 physical 事务)

声明式事务

添加 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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
	http://www.springframework.org/schema/tx
	http://www.springframework.org/schema/tx/spring-tx.xsd
	http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-2.0.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.driverClassName}" />
    <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" />

定义事务 Advice

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" />
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>

定义 Pointcut

<aop:config>
    <aop:pointcut id="daoOperation"
        expression="execution(* com.netease.course.AccountDao.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="daoOperation" />
</aop:config>

配置 <tx:method />

name: 匹配的函数名称,支持*匹配
propagation:事务传播行为
isolation:事务隔离级别
timeout:超时
read-only:是否只读事务
rollback-for:触发回滚的异常,用逗号分隔
no-rollback-for:不触发回滚的异常

采用 annotation 方法
@Transactional
xml 中添加

<tx:annotation-driven transaction-manager="txManager" />

相应代码

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public boolean deleteClusterByClusterId(Sring clusterId) {
	// do work
}

@transactional
value:使用的 TransactionManager
propagation:事务传播行为
isolation:事务隔离级别
timeout:超时
readOnly:是否只读
rollbackFor:触发回滚的异常类对象数组
rollbackForClassName:触发回滚的异常类名称数组
noRollbackFor:不触发回滚的异常类对象数组
noRollbackForClassName:不触发回滚的异常类名称数组

编程式事务

定义 TransactionTemplate

public class SimpleService implements Service {
	private final TransactionTemplate transactionTemplate;
	public SimpleService(PlatformTransactionManager transactionManager) {
		this.transactionTemplate = new TransactionTemplate(transactionManager);
		this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
		this.transactionTemplate.setTimeout(30);
	}
}

使用 TransactionTemplate

public Object someMethod() {
    return transactionTemplate.execute(new TransactionCallback() {
        public Object doInTransaction(TransactionStatus status) {
            updateOperation1();
            return resultOfUpdateOperation2();
        }
    });
}

或(不返回结果)

public Object someMethodWithoutResult() {
    return transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            updateOperation1();
            updateOperation2();
        }
    });
}

或(设置为遇到异常时只能回滚)

public Object someMethodWithoutResult() {
    return transactionTemplate
            .execute(new TransactionCallbackWithoutResult() {
                protected void doInTransactionWithoutResult(
                        TransactionStatus status) {
                    try {
                        updateOperation1();
                        updateOperation2();
                    } catch (SomeBusinessException e) {
                        status.setRollbackOnly();
                    }
                }
            });
}

PlatformTransactionManager 的实现

DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("TxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

TransactionStatus status = txManager.getTransaction(def);
try {
    //do something
} catch (MyException ex) {
    txManager.rollback(status);
    throw ex;
}
txManager.commit(status);

整合 MyBatis

SqlSessionFactory
添加 mybatis-spring 依赖

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.3.0</version>
    </dependency>

定义 SqlSessionFactoryBean

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:sqlMapConfig.xml" />
    <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml" />
</bean>

定义 Mapper

public interface UserMapper {

	@Select("SELECT * FROM users WHERE id = #{userId}")
	User getUser(@Param("userId") String userId);
}

配置结果映射

@Results({
    @Result(property="id", column="id"),
    @Result(property="firstName", column="first_name"),
    @Result(property="lastName", column="last_name")
})

或者采用 xml 的方法,见 Mybatis 部分

定义 Mapper Bean

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

或采用自动发现的机制

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://mybatis.org/schema/mybatis-spring
	http://mybatis.org/schema/mybatis-spring.xsd">

	<mybatis:scan base-package="com.netease.course" />
</beans>

当需要指定 SqlSessionFactory 时

<mybatis:scan base-package="com.netease.course" factory-ref="sqlSessionFactory" />

或者

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.netease.course" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>

使用 Mapper

public class SomeService {
	@Autowired
	private UserMapper userMapper;
	public User getUser(String userId) {
		return userMapper.getUser(userId);
	}
}

定义 SqlSessionTemplate 使用

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

SqlSessionTemplate 使用

public class UserDao {
	@Autowired
	private SqlSession sqlSession;
	public User getUser(String userId) {
		return (User) sqlSession.selectOne("com.netease.course.UserMapper.getUser", userId);
	}
}

//Web 框架

DispatcherServlet

[servlet-name]-servlet.xml
HandlerMapping
Controllers
View 解析相关

WebApplicationContext

ContextLoaderListener

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>

Servlet WebApplicationContext (containing controllers, view resolvers, and other web-related beans)
Root WebAppliacationContext (containing middler-tier services, datasources, etc.)

实现 Controller

  @Controller
  @RequestMapping(value = "/hello")
  public class HelloController {
	  @RequestMapping(value = "/spring")
	  public void spring(HttpServletResponse response) throws IOException {
		  response.getWriter().write("Hello, Spring Web!!");
	  }
  }

定义 Controller

自动发现

 <context:component-scan base-package="com.netease.course" />

@RequestMapping
name: 名称
value & path: 路径,如"/hello"
method: 请求方法,如"GET"
params: 请求参数
headers: 请求头
consumes: 请求的媒体类型,"Content-Type"
produces: 响应的媒体类型,"ACCEPT"

注入路径中的变量

@RequestMapping(value="/users/{userId}")
public String webMethod(@PathVariable String userId) {
    //do work
}

@RequestMapping(value="/users/{userId:[a-z]+}")
public String webMethod(@PathVariable String userId) {
    //do work
}

函数参数
HttpServletRequest / HttpServletResponse, HttpSession (Servlet API)
Reader / Writer
@PathVariable
@RequestParam
@RequestHeader
HttpEntity
@RequestBody
Map / Model / ModelMap

函数返回值
void
String: view 名称,@ResponseBody
HttpEntity
View
Map
Model
ModelAndView

函数实现

@RequestMapping(value="/spring/{user}")

public void helloSpring(

        @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(@ModelAttribute User user, Writer writer) {

    //do work

}

@RequestMapping(value="/users/login")

public String login(@RequestParam("name") String name, @RequestParam("password") String password, ModelMap map) {

    map.addAttribute("name", name);

    map.addAttribute("password", "******");

    return "user";

}

ModelMap
ModelMap 的实例是由 bboss mvc 框架自动创建并作为控制器方法参数传入,用户无需自己创建。

public String xxxxmethod(String someparam,ModelMap model)   
{   
	 //省略方法处理逻辑若干   
	  //将数据放置到ModelMap对象model中,第二个参数可以是任何java类型   
	  model.addAttribute("key",someparam);   
	 ......   
	 //返回跳转地址   
	  return "path:handleok";   
}  

ModelAndView
ModelAndView 的实例是由用户手动创建的,这也是和 ModelMap 的一个区别。

public ModelAndView xxxxmethod(String someparam)   
{   
	 //省略方法处理逻辑若干   
	  //构建ModelAndView实例,并设置跳转地址   
	  ModelAndView view = new ModelAndView("path:handleok");   
	  //将数据放置到ModelAndView对象view中,第二个参数可以是任何java类型   
	  view.addObject("key",someparam);   
	 ......   
	 //返回ModelAndView对象view   
	  return view;   
}  

上传文件

定义 bean

<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="100000" />
</bean>

相应代码

@RequestMapping(value="/form", method=RequestMethod.POST)
public String handleFormUpload(@RequestParam("file") MultipartFile file) {
    // save the file
}

相应依赖

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

HttpEntity

@RequestMapping(value="/something")
public ResponseEntity<String> handle(HttpEntity<byte[]> requestEntity) {
    String requestHeader = requestEntity.getHeaders().getFirst("MyRequestHeader");
    byte[] requestBody = requestEntity.getBody();
    // do something with requestHeader and requestBody
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.set("MyResponseHeader", "MyValue");
    return new ResponseEntity<String>("hello spring", responseHeaders, HttpStatus.CREATED);
}

@RequestBody & @ResponseBody

@RequestMapping(value="/spring")
@ResponseBody
public String spring(@RequestBody String body) throws IOException {
    return "hello" + body;
}

MessageConverter,返回 Java 对象的转化
RequestBody ---> Object: 参数
ResponseBody <--- Object: 返回值

xml 文件配置

<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:mvc=“http://www.springframework.org/schema/mvc”
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="com.netease.course" />
	<mvc:annotation-driven />
</beans>

添加相应依赖,如 JSON 相关依赖

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.4</version>
    </dependency>

View 解析

String 名称

View

ModelAndView

Map

Model

ViewResolver (org.springframework.web.servlet):

InternalResourceViewResolver (org.springframework.web.servlet.view)
FreeMarkerViewResolver (org.springframework.web.servlet.view.freemarker)
ContentNegotiatingViewResolver (org.springframework.web.servlet.view)

InternalResourceViewResovler
Servlet, JSP
bean 定义

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

resultView -------> /WEB-INF/jsp/resultView.jsp

FreeMarkerViewResolver
FreeMarker
bean 定义

<bean id="freemarkerConfig"
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/" />
</bean>
<bean id="viewResolver"
    class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="cache" value="true" />
    <property name="prefix" value="" />
    <property name="suffix" value=".ftl" />
    <property name="contentType" value="text/html; charset=utf-8" />
</bean>

ContentNegotiatingViewResolver
ViewResovler 的组合
扩展名:user.json, user.xml, user.pdf
Accept 头(media types):application / json, application / xml
bean 定义

<bean
    class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="viewResolvers">
        <list>
            <bean id="viewResolver"
                class="org.springframework.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>

DefaultRequestToViewNameTranslator
example/admin/index.html ---------> admin/index
example/display.html -------> display

ContentNegotiatingViewResolver 根据路径后缀,选择不同视图

方法一:使用扩展名

http://localhost:8080/learn/user.xml 获取 xml 类型数据

http://localhost:8080/learn/user.json 获取 json 类型数据

http://localhost:8080/learn/user 使用默认 view 呈现,如 jsp

方法二:使用 http 请求头的 Accept

GET /user HTTP/1.1

Accept:application/xml

GET /user HTTP/1.1

Accept:application/json

方法三:使用参数

http://localhost:8080/learn/user?format=xml

http://localhost:8080/learn/user?format=json

同一资源,多种表述

<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">  
    <!-- 是否启用扩展名支持,默认为true -->  
    <property name="favorPathExtenion" value="true" />  
    <!-- 是否启用参数支持,默认为true -->  
    <property name="favorParameter" value="false" />  
    <!-- 是否忽略掉accept header,默认为false -->  
    <property name="ignoreAcceptHeader" value="true" />  
      
    <!-- 扩展名到mimeType的映射 -->  
    <property name="mediaTypes">  
        <map>  
            <!-- 例如:/user.json 中的 .json 会映射到 application/json -->  
            <entry key="json" value="application/json" />  
            <entry key="xml" value="application/xml" />          
        </map>  
    </property>  
      
    <!-- 如果所有mediaType都没匹配上,就使用defaultContentType -->  
    <property name="defaultContentType" value="text/html"/>  
</bean>  
  
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">  
    <!-- 解析器的执行顺序 -->  
    <property name="order" value="1" />  
    <property name="contentNegotiationManager" ref="contentNegotiationManager" />  
      
    <property name="defaultViews">  
        <list>  
            <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"></bean>  
            <bean class="org.springframework.web.servlet.view.xml.MarshallingView">  
                <constructor-arg>  
                    <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">  
                        <property name="classesToBeBound">  
                            <list>  
                                <value>com.learn.model.User</value>  
                            </list>  
                        </property>  
                    </bean>  
                </constructor-arg>  
            </bean>  
        </list>  
    </property>  
      
</bean>  
<!-- 上面没匹配到则会使用这个视图解析器 ,解析为jsp  -->  
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
    <property name="order" value="2" />  
    <property name="prefix" value="/" />   
    <property name="suffix" value=".jsp"/>  
</bean>  

Controller 中可以用 RequestMapping 匹配多个路径后缀

@RequestMapping(value = {"/users", "/users.html", "/users.json"})

public String getUsersInfo(ModelMap map) {

    List users = userServiceImp.getUsers();

    map.addAttribute("users", users);

    return "users";

}

FreeMarker

模板引擎

数据模型

对象:hashes

基本类型:scalars

注释

<#-- 这是注释 -->

插值:表达式

${animal.name}

直接指定值:

字符串,如“Zoo”, 'Zoo'

数字,如123.45

布尔值,如true,false

序列,如["zoo", "bar", 123]

值域,如0..9,0..<10

哈希表,如{"name":"green mouse", "price":150}

检索变量:

顶层变量:user

哈希表数据:user.name, user["name"]

列表数据:products[5]

连接操作:

users + "guest"

passwords + "joe":"secret123"

算术操作

逻辑操作

比较操作

FTL 标签:指令

开始标签:<#directivename parameters>

结束标签:

if 指令

<#if user=="Big Joe">, our beloved leader

list 指令

<#list animals as animal>

  ${animal.name}${animal.price} Euros

include 指令

<#include "/copyright_footer.html">

使用

创建配置

Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);

cfg.setDirectoryForTemplateLoading(new File("/where/you/store/templates"));

cfg.setDefaultEncoding("UTF-8");

cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

定义模板

定义数据模型

Map root = new HashMap<>();

root.put("user", "Big Joe");

Map latestProduct = new HashMap<>();

root.put("latestProduct", latestProduct);

latestProduct.put("url", "products/greenmouse.html");

latestProduct.put("name", "greenmouse");

添加依赖

  <dependency>

      <groupId>org.freemarkergroupId>

      <artifactId>freemarkerartifactId>

      <version>2.3.23version>

  dependency>

输出结果

    Template ftl = cfg.getTemplate("user.ftl");

    Writer outWriter = new OutputStreamWriter(System.out);

    ftl.process(root, outWriter);

工程模板

一般 java 目录

com.netease.course.dao

com.netease.course.meta

com.netease.course.service

com.netease.course.service.impl

com.netease.course.utils

com.netease.course.web.controller

com.netease.course.web.filter

  • Web
    115 引用 • 431 回帖 • 8 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    940 引用 • 1458 回帖 • 159 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3167 引用 • 8207 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    51 引用 • 226 回帖
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    915 引用 • 931 回帖
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    77 引用 • 1741 回帖
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 2 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 67 关注
  • ngrok

    ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。

    7 引用 • 63 回帖 • 596 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    6498 引用 • 29198 回帖 • 248 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖 • 1 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    139 引用 • 441 回帖
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 132 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 603 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 439 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖 • 1 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 592 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    131 引用 • 1114 回帖 • 149 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    205 引用 • 357 回帖
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 426 关注
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 695 关注
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 12 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 437 关注
  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖 • 1 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 745 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 43 关注
  • 资讯

    资讯是用户因为及时地获得它并利用它而能够在相对短的时间内给自己带来价值的信息,资讯有时效性和地域性。

    53 引用 • 85 回帖