<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.6.2">Jekyll</generator><link href="https://yumao123.github.io/atom.xml" rel="self" type="application/atom+xml" /><link href="https://yumao123.github.io/" rel="alternate" type="text/html" /><updated>2017-12-11T11:18:53+00:00</updated><id>https://yumao123.github.io/</id><title type="html">YumaoZzz</title><author><name>Yumao</name></author><entry><title type="html">java visualVM 以及其他jvm性能监控工具使用</title><link href="https://yumao123.github.io/others/2017/12/11/java-visualVM%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/" rel="alternate" type="text/html" title="java visualVM 以及其他jvm性能监控工具使用" /><published>2017-12-11T00:00:00+00:00</published><updated>2017-12-11T00:00:00+00:00</updated><id>https://yumao123.github.io/others/2017/12/11/java%20visualVM%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95</id><content type="html" xml:base="https://yumao123.github.io/others/2017/12/11/java-visualVM%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/">&lt;blockquote&gt;
  &lt;p&gt;关于visualVM以及其他jvm性能监控工具使用的一些使用记录
&lt;img src=&quot;https://yumao123.github.io/assets/images/201712/1211_top.png&quot; alt=&quot;TITLE&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;visualvm远程连接jvm&quot;&gt;visualVM远程连接jvm&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;添加JMX连接&lt;br /&gt;
关于jmx：JMX的全称为Java Management Extensions. &lt;br /&gt;顾名思义，是管理Java的一种扩展。这种机制可以方便的管理、监控正在运行中的Java程序。常用于管理线程，内存，日志Level，服务重启，系统环境等&lt;br /&gt;
1：确保jvm启动时的参数&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=xxxx &amp;lt;- jmx连接时的端口
-Dcom.sun.management.jmxremote.ssl=false &amp;lt;- ssl安全连接,默认为是
-Dcom.sun.management.jmxremote.authenticate=false &amp;lt;- 不需要认证
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;2：使用visualvm连接&lt;br /&gt;
先建立对应的远程连接，之后添加JMX连接&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;添加jstatd连接
1：修改$JAVA_HOME/jre/lib/security/java.policy中，在最后一行添加
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;permission java.security.AllPermission;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;2：启动jstatd服务&lt;/p&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.logCalls=true -J-Djava.rmi.server.hostname=139.199.182.196
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;3：添加jstatd连接，visualVM会自动连接&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;jconsole&quot;&gt;jconsole&lt;/h2&gt;
&lt;p&gt;查询jvm的cpu&amp;amp;内存&lt;br /&gt;
需要jvm启动时加入命令&lt;br /&gt;&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=xxxx &amp;lt;- jmx连接时的端口
-Dcom.sun.management.jmxremote.ssl=false &amp;lt;- ssl安全连接,默认为是
-Dcom.sun.management.jmxremote.authenticate=false &amp;lt;- 不需要认证
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;jstat&quot;&gt;jstat&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://yumao123.github.io/assets/images/201712/jvmtools1.png&quot; alt=&quot;TITLE&quot; /&gt;
从左到右代表值：&lt;br /&gt;
第一个sur的容量，第二个sur的容量，第一个sur已使用空间，第二个sur已使用空间&lt;br /&gt;
eden容量，eden已使用空间，old代容量，old代已使用空间&lt;br /&gt;
方法区大小，方法区使用大小，压缩类空间大小，压缩类空间似乎用大小&lt;br /&gt;
年轻代gc次数，年轻代gc时间，old代gc次数，old代gc时间，gc总时间&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;jmap&quot;&gt;jmap&lt;/h2&gt;

&lt;h2 id=&quot;jstack&quot;&gt;jstack&lt;/h2&gt;
&lt;p&gt;实例:发现某个进程cpu过高,想找到对应的线程,打印线程栈
1：先查找对应的进程&lt;code class=&quot;highlighter-rouge&quot;&gt;top&lt;/code&gt;
1：再通过top查找对应的线程&lt;code class=&quot;highlighter-rouge&quot;&gt;top -H -p pid&lt;/code&gt;
2：通过jstack打印对应线程栈信息&lt;code class=&quot;highlighter-rouge&quot;&gt;jstack pid&lt;/code&gt;
遇到的问题:&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10555: Unable to open socket file: target process not responding or HotSpot VM not loaded
The -F option can be used when the target process is not responding
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Yumao</name></author><category term="Blog" /><summary type="html">关于visualVM以及其他jvm性能监控工具使用的一些使用记录</summary></entry><entry><title type="html">Spring NoSQL</title><link href="https://yumao123.github.io/java/2017/11/27/spring-NoSQL%E6%95%B0%E6%8D%AE%E5%BA%93/" rel="alternate" type="text/html" title="Spring NoSQL" /><published>2017-11-27T00:00:00+00:00</published><updated>2017-11-27T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/11/27/spring%20NoSQL%E6%95%B0%E6%8D%AE%E5%BA%93</id><content type="html" xml:base="https://yumao123.github.io/java/2017/11/27/spring-NoSQL%E6%95%B0%E6%8D%AE%E5%BA%93/">&lt;blockquote&gt;
  &lt;p&gt;Spring NoSQL
关于spring data mongodb/redis的基础使用
&lt;img src=&quot;https://yumao123.github.io/assets/images/201711/1127_top.png&quot; alt=&quot;TOP&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;
&lt;p&gt;文档数据库&lt;br /&gt;
适合做：数据不需要分散到多个表or实体中，将信息收集到非规范化的结构更有意义；文档是独立的实体&lt;br /&gt;
eg：学生成绩单，不同学生相互独立，其相互的成绩也没有必要关联&lt;br /&gt;
不适合做：具有明显关联关系的数据&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;使用mongodb&quot;&gt;使用mongodb&lt;/h2&gt;
&lt;p&gt;spring data mongodb提供了三种方式在spring中使用mongodb&lt;br /&gt;
通过注解实现对象-文档映射；&lt;br /&gt;
使用MongoTemplate实现基于模板的数据库访问；&lt;br /&gt;
自动化的运行时Repository生成功能。&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;启用mongodb&lt;br /&gt;
```
编写mongodb配置类
@Configuration
@EnableMongoRepositories(
      basePackages = “spitter.db”
)
/**&lt;/li&gt;
  &lt;li&gt;MongoTemplate:数据库与代码之间的接口，所有操作在这里面&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;通过继承AbstractMongoConfiguration，其回隐式的创建MongoTemplate
*/
public class MongoConfig{
  @Bean
  public MongoClientFactoryBean mongoClientFactoryBean(){
      MongoClientFactoryBean mongoClientFactoryBean = new MongoClientFactoryBean();
      mongoClientFactoryBean.setHost(“localhost”);
      return mongoClientFactoryBean;
  }
  @Bean
  public MongoOperations mongoTemplate(Mongo mongo){
      return new MongoTemplate(mongo, “OrdersDB”);
  }
}
```&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;为模型添加注解，持久化文档
```
/**&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Document：借助MongoTemplate自动生成Repository持久化
*
*/
@Document
public class Order implements Serializable{
  @Field(value = “username”)
  private String username;
  @PersistenceConstructor
  public Order(String username, String id) {
      this.username = username;
      this.id = id;
  }
  @Id
  private String id;
  public String getId() {
      return id;
  }
  public void setId(String id) {
      this.id = id;
  }
  public String getUsername() {
      return username;
  }
  public void setUsername(String username) {
      this.username = username;
  }
}
```&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;使用Mongotemplate访问
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Repository
public class OrderRepository {
  @Autowired
  private MongoOperations template;
  public List&amp;lt;Order&amp;gt; getOrders(){
      int id = (new Random().nextInt(11000));
      template.save(new Order(&quot;mike&quot;, String.valueOf(id)));
      return template.findAll(Order.class);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;注：&lt;br /&gt;
1：遇到问题，spring data mongodb与spring的版本兼容问题&lt;br /&gt;
2：还需要引入mongo-java-driver&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用redis操作key-value数据&quot;&gt;使用redis操作key-value数据&lt;/h2&gt;
&lt;p&gt;提供了4种客户端连接工厂&lt;br /&gt;
JedisConnectionFactory&lt;br /&gt;
JredisConnectionFactory&lt;br /&gt;
LettuceConnectionFactory&lt;br /&gt;
SrpConnectionFactory&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;连接redis&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @Bean
  public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig){
      JedisConnectionFactory jf = new JedisConnectionFactory();
      jf.setPoolConfig(jedisPoolConfig);
      jf.setHostName(&quot;localhost&quot;);
      jf.setPort(6379);
      jf.afterPropertiesSet();
      return jf;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;使用redisTemplate&lt;br /&gt;
提供了两个模板&lt;br /&gt;
RedisTemplate&lt;br /&gt;
StringRedisTemplate&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @Bean
  public RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate(RedisConnectionFactory redisConnectionFactory){
      RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate = new RedisTemplate&amp;lt;String, String&amp;gt;();
      redisTemplate.setConnectionFactory(redisConnectionFactory);
      redisTemplate.afterPropertiesSet();
      return redisTemplate;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;使用key value序列器&lt;br /&gt;
stringRedisTemplate默认使用String序列化策略
redisTemplate默认使用JdkSerializationRedisSerializer序列化策略&lt;br /&gt;
指定序列化策略：
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;默认提供的序列化策略：
GenericToStringSerializer：使用Spring转换服务进行序列化；
JacksonJsonRedisSerializer：使用Jackson 1，将对象序列化为JSON；
Jackson2JsonRedisSerializer：使用Jackson 2，将对象序列化为JSON；
JdkSerializationRedisSerializer：使用Java序列化；
OxmSerializer：使用Spring O/X映射的编排器和解排器（marshaler和unmarshaler）实现序列化，用于XML序列化；
StringRedisSerializer：序列化String类型的key和value。
  @Bean
  public RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate(RedisConnectionFactory redisConnectionFactory){
      RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate = new RedisTemplate&amp;lt;String, String&amp;gt;();
      redisTemplate.setConnectionFactory(redisConnectionFactory);
      redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer&amp;lt;Order&amp;gt;(Order.class));
      redisTemplate.afterPropertiesSet();
      return redisTemplate;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;否则如果使用默认的redisTemplate可能会乱码&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>Yumao</name></author><category term="Spring" /><category term="Sql" /><summary type="html">Spring NoSQL 关于spring data mongodb/redis的基础使用</summary></entry><entry><title type="html">Spring 缓存</title><link href="https://yumao123.github.io/java/2017/11/22/spring-%E7%BC%93%E5%AD%98/" rel="alternate" type="text/html" title="Spring 缓存" /><published>2017-11-22T00:00:00+00:00</published><updated>2017-11-22T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/11/22/spring%20%E7%BC%93%E5%AD%98</id><content type="html" xml:base="https://yumao123.github.io/java/2017/11/22/spring-%E7%BC%93%E5%AD%98/">&lt;blockquote&gt;
  &lt;p&gt;Spring缓存
spring都有哪几种方式能实现缓存？为什么以及什么情况下需要用到、更新、删除缓存
&lt;img src=&quot;https://yumao123.github.io/assets/images/201711/1123_top.png&quot; alt=&quot;TOP&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;启用支持&quot;&gt;启用支持&lt;/h2&gt;
&lt;p&gt;1：注解驱动的缓存 @Cacheable @CacheEvict&lt;br /&gt;
2：xml声明的缓存&lt;br /&gt;
如果使用java配置，需要在其中一个配置类上添加@EnableCaching&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@EnableCaching
@Configuration
public class CacheConfig {
    @Bean
    // 声明缓存管理器
    public CacheManager cacheManager(){
        return new ConcurrentMapCacheManager();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;如果使用xml配置&lt;cache:annotation-driven&gt; (启用缓存)&lt;br /&gt;
本质上，都是创建一个切面并触发spring缓存注解的切点。根据所使用的注解以及缓存状态，这个切面会从缓存获取数据，将数据添加到缓存或从缓存中移除&lt;br /&gt;
ConcurrentMapCacheManager这个简单的缓存管理器使用java.util.concurrent.ConcurrentHashMap作为缓存存储（测试，基于内存）&lt;/cache:annotation-driven&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;配置缓存管理器&lt;br /&gt;
SimpleCacheManager&lt;br /&gt;
NoOpCacheManager&lt;br /&gt;
ConcurrentMapCacheManager&lt;br /&gt;
CompositeCacheManager&lt;br /&gt;
EhCacheCacheManager&lt;br /&gt;
RedisCacheManager（来自于Spring Data Redis项目）&lt;br /&gt;
GemfireCacheManager（来自于Spring Data GemFire项目）&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1：使用ehcache&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;EhcacheConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * 声明一个CacheManager bean
     * Spring提供了EhCacheManager-FactoryBean来生成EhCache的CacheManager
     *
     */&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EhCacheManagerFactoryBean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ehcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;EhCacheManagerFactoryBean&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EhCacheManagerFactoryBean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;eb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;setConfigLocation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ClassPathResource&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;spittr/cache/ehcache.xml&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;eb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/**
     * 注：
     * org.springframework.cache.ehcache
     * 需要导入spring-context-support，且还需要额外导入net.sf.ehcache
     * 通过传入EhcacheCacheManager实例实现
     * EhCache的CacheManager要被注入到Spring的EhCacheCacheManager
     *
     */&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;EhCacheCacheManager&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cacheManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheManager&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;){&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;EhCacheCacheManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ehcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;xml&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ehcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;spittleCache&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;maxBytesLocalHeap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;50m&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeToLiveSeconds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ehcache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2:使用redis缓存&lt;br /&gt;
如果缓存的条目是键值对形式的，那非常适合用redis存储&lt;br /&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;需要引入org.springframework.data.reids &amp;amp; redis.client
redis提供了RedisCacheManager，是CacheManager的实现，会与redis服务器协作，通过RedisTemplate将缓存条目存储到Redis中
    @Bean
    public JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxWaitMillis(1000);
        return jedisPoolConfig;
    }
    @Bean
    public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig){
        JedisConnectionFactory jf = new JedisConnectionFactory();
        jf.setPoolConfig(jedisPoolConfig);
        jf.setHostName(&quot;localhost&quot;);
        jf.setPort(6379);
        jf.afterPropertiesSet();
        return jf;
    }
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate){
        return new RedisCacheManager(redisTemplate);
    }
    @Bean
    public RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate&amp;lt;String, String&amp;gt; redisTemplate = new RedisTemplate&amp;lt;String, String&amp;gt;();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
注：报错// DefaultSerializer requires a Serializable payload but received an object of type [spitter.Spittle]
对应类必须实现Serializable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;为方法添加注解支持缓存&quot;&gt;为方法添加注解支持缓存&lt;/h2&gt;
&lt;p&gt;@Cacheable 表明Spring在调用方法之前，首先应该在缓存中查找方法的返回值。如果这个值能够找到，就会返回缓存的值。否则的话，这个方法就会被调用，返回值会放到缓存之中&lt;br /&gt;
@CachePut 表明Spring应该将方法的返回值放到缓存中。在方法的调用前并不会检查缓存，方法始终都会被调用&lt;br /&gt;
@CacheEvict 表明Spring应该在缓存中清除一个或多个条目&lt;br /&gt;
@Caching 这是一个分组的注解，能够同时应用多个其他的缓存注解&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;填充缓存&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    /**
    * 缓存切面会拦截调用并在缓存中查找之前以名spittleCache存储的返回值
    * 缓存的key是传递到findOne()方法中的id参数
    * 如果按照这个key能够找到值的话，就会返回找到的值，方法不会再被调用
    * 但是这样仅限于这个实现类，为了避免出现这个问题，可以将Cacheable声明在接口方法上
    * 带有@CachePut的方法始终会被调用，可以实现预加载
    * 例如，可以在save(Spittle spittle)方法上添加@CachePut(&quot;spittleCache&quot;)
    * 但是这样回导致key为Spittle，所以需要指定key
    * @CachePut(&quot;spittleCache&quot;, key=&quot;#result.id&quot;) &amp;lt;= 表达式#result能够得到返回的Spittle
    * 此外，还有当数据有修改时，可以通过CachePut强制更新
    */
    @Cacheable(&quot;spittleCache&quot;)
    public Spittle findSpittels(int id) {
        System.out.println(&quot;调用一次:&quot; + id);
        return jdbc.queryForObject(&quot;select message, created_at, longitude,latitude from Spittle where id = ?&quot;,
                new SpitterRowMapper(),
                id);
    }
注：这里遇到一个问题，1.7.0的spring-data-redis返回RedisCacheManager对象，其父类没有实现CacheManager接口，是因为没有spring-support的jar或者对应jar的版本与spring版本不匹配
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;条件化缓存&lt;br /&gt;
某些情况下，可能只有符合某些条件下我们才会使用缓存&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    /**
    * unless:如果为true,阻止将对象放入缓存，但是如果缓存中存在，依然从缓存中读取
    * condition:如果condition返回false,禁用缓存
    */
    @CachePut(value = &quot;spitterCache&quot;, key = &quot;#result.username&quot;, unless = &quot;#result.username.contains('NoCache')&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Yumao</name></author><category term="Spring" /><category term="Cache" /><summary type="html">Spring缓存 spring都有哪几种方式能实现缓存？为什么以及什么情况下需要用到、更新、删除缓存</summary></entry><entry><title type="html">Spring 持久化数据</title><link href="https://yumao123.github.io/java/2017/11/16/spring-%E6%8C%81%E4%B9%85%E5%8C%96%E6%95%B0%E6%8D%AE/" rel="alternate" type="text/html" title="Spring 持久化数据" /><published>2017-11-16T00:00:00+00:00</published><updated>2017-11-16T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/11/16/spring%20%E6%8C%81%E4%B9%85%E5%8C%96%E6%95%B0%E6%8D%AE</id><content type="html" xml:base="https://yumao123.github.io/java/2017/11/16/spring-%E6%8C%81%E4%B9%85%E5%8C%96%E6%95%B0%E6%8D%AE/">&lt;blockquote&gt;
  &lt;p&gt;Spring的持久化数据
spring中持久化数据的方式很多，本文主要介绍几种配置datasource的方法，以及如何通过spring内置的jdbc模板实现数据的持久化
&lt;img src=&quot;https://yumao123.github.io/assets/images/201711/1116_top.png&quot; alt=&quot;TOP&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;配置数据源&quot;&gt;配置数据源：&lt;/h2&gt;
&lt;p&gt;通过JDBC驱动程序定义的数据源；&lt;br /&gt;
通过JNDI查找的数据源；&lt;br /&gt;
连接池的数据源；&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;JNDI：&lt;br /&gt;
这种配置的好处在于数据源完全可以在应用程序之外进行管理，这样应用程序只需在访问数据库的时候查找数据源就可以了。另外，在应用服务器中管理的数据源通常以池的方式组织，从而具备更好的性能，并且还支持系统管理员对其进行热切换
通过&lt;jee:jndi-lookup&gt;装配
```&lt;/jee:jndi-lookup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;jee:jndi-lookup jndi-name=&quot;jndiDS&quot; id=&quot;dataSourceJndi&quot; resource-ref=&quot;true&quot; /&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- 连接池：&amp;lt;br&amp;gt;
开源库：Apache Commons DBCP、c3p0、BoneCP
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&amp;lt;bean id=”database” class=”xxx.dbcp.BasicDataSource” p:driverClassName=”xx” …&amp;gt;&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- 基于JDBC驱动的数据源：&amp;lt;br&amp;gt;
spring提供了3个数据源类
DriverManagerDataSource（在每个连接请求时都会返回一个新建的连接）、SimpleDriverDataSource、SingleConnectionDataSource（在每个连接请求时都会返回同一个的连接）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&amp;lt;bean id=”dataSource” calss=”xxx.DriverManagerDataSource” p:dirverClassName=”xx” …&amp;gt;&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;- 使用嵌入式的数据源：&amp;lt;br&amp;gt;
优势（在每次启动服务时，都能重新填充数据）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;jdbc:embedded-database id=&quot;dataSourceEm&quot; type=&quot;H2&quot;&amp;gt;
    &amp;lt;jdbc:script location=&quot;schema.sql&quot;/&amp;gt;
&amp;lt;/jdbc:embedded-database&amp;gt; ``` - 通过profile选择数据源&amp;lt;br&amp;gt; 只要在配置bean前，添加&amp;lt;beans profile=&quot;dev&quot;&amp;gt;即可
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;在spring中使用jdbc&quot;&gt;在spring中使用jdbc&lt;/h2&gt;
&lt;p&gt;平时在使用jdbc最容易出现的问题：1.代码混乱且很多冗余；2.异常情况无法处理；
解决方法：使用jdbc模板&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;使用jdbc模板&lt;br /&gt;
Spring为JDBC提供了三个模板类供选择&lt;br /&gt;
JdbcTemplate（被废弃）：最基本的Spring JDBC模板，这个模板支持简单的JDBC数据库访问功能以及基于索引参数的查询&lt;br /&gt;
NamedParameterJdbcTemplate：使用该模板类执行查询时可以将值以命名参数的形式绑定到SQL中，而不是使用简单的索引参数&lt;br /&gt;
SimpleJdbcTemplate：该模板类利用Java 5的一些特性如自动装箱、泛型以及可变参数列表来简化JDBC模板的使用&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Bean
public JdbcTemplate jdbcTemplate(DataSource datasource){
  return new JdbcTemplate(datasource);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;如果通过jdbcTemplate查找数据，则需要将结果映射到对象中&lt;/p&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;return jdbc.queryForObject(select_by_id, new SpitterRowMapper(), id);
private static final class SpitterRowMapper implements RowMapper&amp;lt;Spitter&amp;gt;{
  public Spiter mapRow(ResultSet rs, int rowNum){
      return new Spitter(rs.getLong(&quot;id&quot;), ....)
  }
}
其中queryForObject有三个参数
String对象，包含了要从数据库中查找数据的SQL
RowMapper对象，用来从ResultSet中提取数据并构建域对象
可变参数列表，列出了要绑定到查询上的索引参数值
对于查询返回的每一行数据，JdbcTemplate将会调用RowMapper的mapRow()方法，并传入一个ResultSet和包含行号的整数。在SpitterRowMapper的mapRow()方法中，我们创建了Spitter对象并将ResultSet中的值填充进去
还可以通过方法引用、匿名函数的方法处理queryForObject
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;使用命名参数&lt;br /&gt;
以往插入数据时可能是这样
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jdbc.update(&quot;insert into xx (a,b) values (?,?)&quot;, s.getxx(), s.getxx())
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;这样可能会导致如果参数顺序有变化，那么还需要修改从参数值的顺序&lt;br /&gt;
通过命名参数可以避免这种问题&lt;br /&gt;&lt;/p&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;jdbc.update(&quot;insert into xx (id,name) values (:id, :name)&quot;, new HashMap(){put(&quot;name&quot;, s.getName());})
并且要将模板类改为NamedParameterJdbcTemplate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>Yumao</name></author><category term="Spring" /><summary type="html">Spring的持久化数据 spring中持久化数据的方式很多，本文主要介绍几种配置datasource的方法，以及如何通过spring内置的jdbc模板实现数据的持久化</summary></entry><entry><title type="html">Spring web概述</title><link href="https://yumao123.github.io/java/2017/10/30/spring-web%E6%A6%82%E8%BF%B0/" rel="alternate" type="text/html" title="Spring web概述" /><published>2017-10-30T00:00:00+00:00</published><updated>2017-10-30T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/10/30/spring%20web%E6%A6%82%E8%BF%B0</id><content type="html" xml:base="https://yumao123.github.io/java/2017/10/30/spring-web%E6%A6%82%E8%BF%B0/">&lt;blockquote&gt;
  &lt;p&gt;Spring web概述
主要内容有：如何启动springweb；如何建立简单的控制器且返回视图；如何接收处理来自客户端的请求
&lt;img src=&quot;https://yumao123.github.io/assets/images/201711/1101_top.png&quot; alt=&quot;unkown&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;关于流程&quot;&gt;关于流程&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://yumao123.github.io/assets/images/201711/springweb1.png&quot; alt=&quot;截图&quot; /&gt;
1.请求 -&amp;gt; spring前端控制器DispatcherServlet
2.DispatcherServlet -&amp;gt; 查询handlermapping -&amp;gt; controller(将请求发送给对应的控制器)
3.conroller -&amp;gt; 服务对象处理业务逻辑
4.服务对象产生信息模型(model) -&amp;gt; view -&amp;gt; DispatcherServlet -&amp;gt; 视图解析器view resolver匹配视图名为一个特定视图的实现(例如jsp) -&amp;gt; 浏览器&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SpitterWebInit:
/**
 * 在servlet3.0下，容器回去类路径中查找实现javax.servlet.servletcontainerinitializer接口的类
 * spring提供实现这个接口的实现类：springservletcontainerinitializer，反查实现了webapplicationinitializer类将配置任务交给他们完成
 * spring引入了WebApplicationInitializer基础实现，也就是AbstractAnnotationConfigDispatcherServletInitializer
 * 所以只要我们实现了AbstractAnnotationConfigDispatcherServletInitializer，也就同时实现了WebApplicationInitializer
 * 当部署在servlet3.0容器中时，容器会自动发现它，配置servlet上下文
 */
// 扩展AbstractAnnotation-ConfigDispatcherServletInitializer的任意类都会自动地 配置Dispatcher-Servlet和Spring应用上下文，Spring的应用上下
// 文会位于应用程序的Servlet上下文之中
public class SpitterWebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class&amp;lt;?&amp;gt;[] getRootConfigClasses() {
        return new Class[]{RootConfig.class};
    }
    @Override
    // 指定配置类 bean
    protected Class&amp;lt;?&amp;gt;[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }
    @Override
    // DispatcherServlet映射到&quot;/&quot;
    /**
     * DispatcherServlet 和 ContextLoaderListener(servlet监听器)
     * DispatcherServlet启动时 - 创建spring上下文，加载bean(getServletConfigClasses)
     * 还有一个上下文，由ContextLoaderListener创建，加载应用中其他bean(驱动应用后端中间层和数据层组件)
     * GetServlet-ConfigClasses()会返回带有@Configuration注解的类，定义DispatcherServlet应用上下文中的bean
     * getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置ContextLoaderListener创建的应用上下文中的bean
     */
    protected String[] getServletMappings() {
        return new String[]{&quot;/&quot;};
    }
}
WebConfig:
@Configuration
// 启用spring mvc
@EnableWebMvc
@ComponentScan(basePackages = {&quot;spittr&quot;})
public class WebConfig extends WebMvcConfigurationSupport{
    @Bean
    // 配置jsp视图解析器
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix(&quot;/WEB-INF/views/&quot;);
        resolver.setSuffix(&quot;.jsp&quot;);
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }
    @Override
    // 静态资源处理，将对静态资源的请求转发到servlet容器默认的servlet上，而不是使用DispatcherServlet
    protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}
RootConfig:
@Configuration
@ComponentScan(basePackages = {&quot;spittr&quot;}, excludeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION, value= EnableWebMvc.class)})
public class RootConfig {
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;传递模型到视图中&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DataConfig:
@Configuration
public class DataConfig {
  @Bean
  public DataSource dataSource(){
      return new EmbeddedDatabaseBuilder()
              .setType(EmbeddedDatabaseType.H2)
              .addScripts(&quot;schema.sql&quot;, &quot;data.sql&quot;)
              .build();
  }
  @Bean
  public JdbcOperations jdbcTemplate(DataSource dataSource) {
      return new JdbcTemplate(dataSource);
  }
}
JDBCSpittleRepository:
@Repository
public class JDBCSpittleRepository implements SpittleRepository {
  @Autowired
  private JdbcOperations jdbc;
  public List&amp;lt;Spittle&amp;gt; getSpittles() {
      return jdbc.query(&quot;select * from Spittle&quot;, new SpittleRowMapper());
  }
  private static class SpittleRowMapper implements RowMapper&amp;lt;Spittle&amp;gt; {
      public Spittle mapRow(ResultSet rs, int rowNum) throws SQLException {
          return new Spittle(
                  rs.getLong(&quot;id&quot;),
                  rs.getString(&quot;message&quot;),
                  rs.getDate(&quot;created_at&quot;),
                  rs.getDouble(&quot;longitude&quot;),
                  rs.getDouble(&quot;latitude&quot;));
      }
  }
}
Controller:
@Controller
@RequestMapping(&quot;/spittles&quot;)
public class SpittleController {
  @Autowired
  private SpittleRepository spittleRepository;
  @RequestMapping(method = RequestMethod.GET)
  public String spittles(Model model){
      model.addAttribute(&quot;spittleList&quot;, spittleRepository.getSpittles());
      return &quot;spittles&quot;;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;注:@Repository：DAO组件类&lt;br /&gt;
需要通过maven导入h2，spring-jdbc的依赖&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;后端接受请求输入&quot;&gt;后端接受请求输入&lt;/h2&gt;
&lt;p&gt;springmvc接收的请求有&lt;br /&gt;
查询参数（Query Parameter）&lt;br /&gt;
表单参数（Form Parameter）&lt;br /&gt;
路径变量（Path Variable）&lt;br /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;查询参数
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @RequestMapping(method = RequestMethod.GET)
  public List&amp;lt;Spittle&amp;gt; spittles(Model model, @RequestParam(defaultValue = MAX_LONG, value = &quot;max&quot;) Long max){
      System.out.println(max);
      return spittleRepository.getSpittles();
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;路径参数
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @RequestMapping(value = &quot;/{id}&quot;, method = RequestMethod.GET)
  public String spittlesByPath(Model model, @PathVariable(&quot;id&quot;) Long id){
      System.out.println(id);
      model.addAttribute(&quot;spittleList&quot;, spittleRepository.getSpittles());
      return &quot;spittles&quot;;
  }
  注：这里不能直接返回List&amp;lt;Spittle&amp;gt;,因为url最后一个是id,逻辑视图的名称将会根据请求路径推断得出
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;Spring MVC允许我们在@RequestMapping路径中添加占位符。占位符的名称要用大括号（“{”和“}”）括起来。路径中的其他部分要与所处理的请求完全匹配，但是占位符部分可以是任意的值&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;keywords:DispatcherServlet,@EnableWebMvc,ViewResolver,@Repository,@Controller,@RequestMapping,@RequestParam,@PathVariable,Model&lt;/p&gt;

&lt;h2 id=&quot;处理表单&quot;&gt;处理表单&lt;/h2&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    @RequestMapping(value = &quot;register&quot;, method = RequestMethod.POST)
    // 当处理注册表单的POST请求时，控制器需要接受表单数据并将表单数据保存为Spitter对象
    public String register(Spitter spitter){
        spitterRepository.save(spitter);
        // 当InternalResourceViewResolver看到视图格式中的“redirect:”前缀时，它就知道要将其解析为重定向的规则，而不是视图的名称
        return &quot;redirect:/spitter/&quot; + spitter.getUsername();
    }
    @RequestMapping(value = &quot;/{username}&quot;, method = RequestMethod.GET)
    public String showSpitter(@PathVariable(&quot;username&quot;) String username, Model model){
        model.addAttribute(spitterRepository.findByUsername(username));
        return &quot;profile&quot;;
    }
registerForm:
&amp;lt;form method=&quot;POST&quot;&amp;gt;
    First Name: &amp;lt;input type=&quot;text&quot; name=&quot;firstName&quot; /&amp;gt;&amp;lt;br/&amp;gt;
    Last Name: &amp;lt;input type=&quot;text&quot; name=&quot;lastName&quot; /&amp;gt;&amp;lt;br/&amp;gt;
    Email: &amp;lt;input type=&quot;email&quot; name=&quot;email&quot; /&amp;gt;&amp;lt;br/&amp;gt;
    Username: &amp;lt;input type=&quot;text&quot; name=&quot;username&quot; /&amp;gt;&amp;lt;br/&amp;gt;
    Password: &amp;lt;input type=&quot;password&quot; name=&quot;password&quot; /&amp;gt;&amp;lt;br/&amp;gt;
    &amp;lt;input type=&quot;submit&quot; value=&quot;Register&quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;keywords:redirect:&lt;/p&gt;</content><author><name>Yumao</name></author><category term="Spring" /><summary type="html">Spring web概述 主要内容有：如何启动springweb；如何建立简单的控制器且返回视图；如何接收处理来自客户端的请求</summary></entry><entry><title type="html">Spring Aop切面</title><link href="https://yumao123.github.io/java/2017/10/30/spring-AOP%E5%88%87%E9%9D%A2/" rel="alternate" type="text/html" title="Spring Aop切面" /><published>2017-10-30T00:00:00+00:00</published><updated>2017-10-30T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/10/30/spring%20AOP%E5%88%87%E9%9D%A2</id><content type="html" xml:base="https://yumao123.github.io/java/2017/10/30/spring-AOP%E5%88%87%E9%9D%A2/">&lt;blockquote&gt;
  &lt;p&gt;Spring的Aop切面
散布于应用中多处的功能被称为横切关注点，且从概念上与应用的业务逻辑分离，将横切关注点与业务逻辑分离就是面向切面编程要解决的问题
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/1030_top.png&quot; alt=&quot;妹&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;aop概述&quot;&gt;AOP概述&lt;/h2&gt;
&lt;p&gt;一个被划分为模块的应用
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop1.png&quot; alt=&quot;截图&quot; /&gt;
如果要重用通用功能的话，最常见是继承 或 委托，然而两种方式都可能导致系统复杂化&lt;br /&gt;
切面提供了取代这些的另一种方案，更清晰简洁，通过声明的方式定义功能以何种方式在何处应用，无需修改受影响的类。横切关注点可以被模块化为特殊类，这些类就被称为切面。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;术语&lt;br /&gt;
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop2.png&quot; alt=&quot;截图&quot; /&gt;
&lt;strong&gt;通知(advice)&lt;/strong&gt;&lt;br /&gt;
AOP中，切面的工作称为通知，定义了切面是什么以及何时使用(调用前?调用后?抛出异常?)&lt;br /&gt;
分为:前置通知(before) 后置通知(after) 返回通知(after-returning) 异常通知(after-throwing) 环绕通知(around)&lt;br /&gt;
&lt;strong&gt;切点(pointcut)&lt;/strong&gt;&lt;br /&gt;
一个切点不需要通知应用的所有连接点，切点有助于缩小切面所通知的连接点范围，通常使用明确类、方法，或者利用正则定义锁匹配的类、方法指定切点&lt;br /&gt;
&lt;strong&gt;连接点(join point)&lt;/strong&gt;&lt;br /&gt;
连接点是应用执行过程中能够插入切面的一个点，可以是调用方法时，抛出异常，或者修改一个字段时；切面代码可以利用这些点插入到应用的正常流程，添加新行为&lt;br /&gt;
&lt;strong&gt;切面(aspect)&lt;/strong&gt;&lt;br /&gt;
是通知和切点的结合，包括何时以及何处完成功能&lt;br /&gt;
&lt;strong&gt;引入(introduction)&lt;/strong&gt;&lt;br /&gt;
引入允许我们向现有类添加新方法或属性，在不修改现有类的情况下，具有新行为和状态&lt;br /&gt;
&lt;strong&gt;织入(weaving)&lt;/strong&gt;&lt;br /&gt;
是把切面应用到目标对象并创建新代理对象的过程；在目标对象生命周期里有多个点可以织入:编译期(需要特殊的编译器，Aspectj是已这种方式织入)、类加载期(需要特殊的类加载器，Aspectj支持这种方式织入)、运行期(Spring aop是已这种方式织入)&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Spring对AOP支持&lt;br /&gt;
1.基于代理的经典Spring AOP&lt;br /&gt;
2.纯POJO切面&lt;br /&gt;
3.@AspectJ注解驱动的切面&lt;br /&gt;
4.注入式AspectJ切面（适用于Spring各版本）&lt;br /&gt;
关于Spring在运行时通知对象
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop2.png&quot; alt=&quot;截图&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;spring只支持方法级别的连接点&lt;br /&gt;
keywords:通知,切点,连接点,切面,引入,织入&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;通过切点选择连接点&quot;&gt;通过切点选择连接点&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;编写切点&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;concert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Performance&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;perform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;使用AspectJ切点表达式
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop4.png&quot; alt=&quot;截图&quot; /&gt;
表达式*开始表示不关心方法返回值，如果需要匹配切点仅匹配concert包，可使用within()指示器
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop5.png&quot; alt=&quot;截图&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;切点中选择bean&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;执行beanPerformance.perform应用通知，但是bean id限定为woodstock
execution(* concert.Performance.perform() and bean('woodstock'))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用注解创建切面&quot;&gt;使用注解创建切面&lt;/h2&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Component
public class TestPerformance implements Performance{
    public void perform() {
        System.out.println(&quot;TestPerformance perform&quot;);
    }
}
// 通过@Aspect进行标注该类是一个切面(通知类)
// AspectJ提供了5个注解定义通知
// @After @AfterReturning @AfterThrowing @Around @Before
@Aspect
public class Audience {
	// 通过@Pointcut可以在切面内定义可重用的切点
    @Pointcut(&quot;execution(* demo.Performance.perform(..))&quot;)
    public void performance(){}
    @Before(&quot;performance()&quot;)
    public void silenceCellPhones(){
        System.out.println(&quot;silenceCellPhones&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;即使使用AspectJ注解，但是该类也不会被视为切面，所以需要启用AspectJ注解&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;javaconfig:
@Configuration
@ComponentScan
// 启用自动代理功能
@EnableAspectJAutoProxy
public class AopConfig {
	// 声明切面Bean
    @Bean
    public Audience audience(){
        return new Audience();
    }
}
xmlconfig:
&amp;lt;aop:aspectj-autoproxy/&amp;gt;
&amp;lt;bean class=&quot;concert.Audience&quot;/&amp;gt;
``
遇到的问题:&amp;lt;br/&amp;gt;
1.AspectJ使用maven的artifactId应该是aspectjweaver&amp;lt;br/&amp;gt;
2.aspectjweaver的版本需要与当前使用jdk版本一致,即jdk1.8对应的版本号是1.8.x,否则报错&amp;lt;br/&amp;gt;

- 创建环绕通知&amp;lt;br/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Around(&quot;execution(* springdemo.CD.Performance.perform(..))&quot;)
// ProceedingJoinPoint这个对象是必须要有的，因为要在通知中通过它来调用被通知的方法
public void watchPerformance(ProceedingJoinPoint jp){
    try{
        System.out.println(&quot;before...&quot;);
        // 当要将控制权交给被通知的方法时，它需要调用ProceedingJoinPoint的proceed()方法
        // 如果不调用proceed方法，方法就会被阻塞
        jp.proceed();
        System.out.println(&quot;after...&quot;);
    }catch (Throwable e){
        System.out.println(&quot;Exception...&quot;);
    }
} ```
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;处理通知中的参数&lt;br /&gt;
如果被通知的方法时有参数的，并且这些参数需要关注，那么久切片就需要访问这些参数
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop6.png&quot; alt=&quot;截图&quot; /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Aspect
public class TrackCounter {
  private Map&amp;lt;Integer, Integer&amp;gt; trackCounts = new HashMap&amp;lt;Integer, Integer&amp;gt;();
  // args表明传给getPlayCount的int参数也会传给通知
  @Pointcut(&quot;execution(* springdemo.CD.CompactDisc.playTrack(int)) &amp;amp;&amp;amp; args(trackNumber)&quot;)
  public void trackPlayed(int trackNumber){}

  @Before(&quot;trackPlayed(trackNumber)&quot;)
  public void countTrack(int trackNumber){
      int currentCount = getPlayCount(trackNumber);
      currentCount ++;
      trackCounts.put(trackNumber, currentCount);
  }
  public int getPlayCount(int trackNumber){
      return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;通过注解引入新功能&lt;br /&gt;
由于java不是动态语言，所以不能像Python一样为对象或类动态添加方法，但是可以通过切片为spring bean添加新方法&lt;br /&gt;
相当于切片实现了包装bean相同接口的代理，如果代理能暴露新接口&lt;br /&gt;
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/aop7.png&quot; alt=&quot;截图&quot; /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;接口
public interface Encoreable {
  void performEncore();
}
实现类
public class EncoreableImpl implements Encoreable {
  public void performEncore() {
      System.out.println(&quot;performEncore&quot;);
  }
}
切面
@Aspect
public class EncoreableConfig {
  // 通过@DeclareParents注解，将Encoreable接口引入到Performance bean中
  @DeclareParents(value=&quot;springdemo.CD.Performance+&quot;, defaultImpl = EncoreableImpl.class)
  public static Encoreable encoreable;
}
测试
@Test
public void testPerform(){
  ((Encoreable) performance).performEncore();
  performance.perform();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;@DeclareParents注解由三部分组成：
value属性指定了哪种类型的bean要引入该接口。在本例中，也就是所有实现Performance的类型。（标记符后面的加号表示是Performance的所有子类型，而不是Performance本身。）&lt;br /&gt;
defaultImpl属性指定了为引入功能提供实现的类。在这里，我们指定的是DefaultEncoreable提供实现。&lt;br /&gt;
@DeclareParents注解所标注的静态属性指明了要引入了接口。在这里，我们所引入的是Encoreable接口&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;keywords:@Aspect,execution,@After,@AfterReturning,@AfterThrowing,@Around,@Before,@Pointcut,@DeclareParents&lt;/p&gt;

&lt;h2 id=&quot;在xml中声明切面&quot;&gt;在xml中声明切面&lt;/h2&gt;
&lt;p&gt;如果要声明切面，但是又不能为类添加注解时，必须转向xml配置
配置方式:&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;aop:advisor&amp;gt;定义AOP通知器
&amp;lt;aop:after&amp;gt;定义AOP后置通知（不管被通知的方法是否执行成功）
&amp;lt;aop:afterreturning&amp;gt;定义AOP返回通知
&amp;lt;aop:afterthrowing&amp;gt;定义AOP异常通知
&amp;lt;aop:around&amp;gt;定义AOP环绕通知
&amp;lt;aop:aspect&amp;gt;定义一个切面
&amp;lt;aop:aspectjautoproxy&amp;gt;启用@AspectJ注解驱动的切面
&amp;lt;aop:before&amp;gt;定义一个AOP前置通知
&amp;lt;aop:config&amp;gt;顶层的AOP配置元素。大多数的&amp;lt;aop:*&amp;gt;元素必须包含在&amp;lt;aop:config&amp;gt;元素内
&amp;lt;aop:declareparents&amp;gt;以透明的方式为被通知的对象引入额外的接口
&amp;lt;aop:pointcut&amp;gt;定义一个切点
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &amp;lt;bean id=&quot;audience&quot; class=&quot;springdemo.CD.Audience&quot;/&amp;gt;
    &amp;lt;bean id=&quot;testPerformance&quot; class=&quot;springdemo.CD.TestPerformance&quot;/&amp;gt;
    &amp;lt;aop:config&amp;gt;
        &amp;lt;aop:aspect ref=&quot;audience&quot;&amp;gt;
            &amp;lt;aop:pointcut id=&quot;perform&quot; expression=&quot;execution(* demo.Performance.perform(..))&quot;/&amp;gt;
            &amp;lt;!--&amp;lt;aop:before method=&quot;silenceCellPhones&quot; pointcut-ref=&quot;perform&quot;/&amp;gt;--&amp;gt;
            &amp;lt;!-- 环绕 --&amp;gt;
            &amp;lt;aop:around method=&quot;watchPerformance&quot; pointcut-ref=&quot;perform&quot;/&amp;gt;
            &amp;lt;!-- 为被通知类引入新功能 --&amp;gt;
            &amp;lt;aop:declare-parents types-matching=&quot;demo.Performance+&quot; implement-interface=&quot;demo.Encoreable&quot; default-impl=&quot;demo.DefaultEncoreable&quot;/&amp;gt;
        &amp;lt;/aop:aspect&amp;gt;
    &amp;lt;/aop:config&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Yumao</name></author><category term="Spring" /><summary type="html">Spring的Aop切面 散布于应用中多处的功能被称为横切关注点，且从概念上与应用的业务逻辑分离，将横切关注点与业务逻辑分离就是面向切面编程要解决的问题</summary></entry><entry><title type="html">Spring装配bean</title><link href="https://yumao123.github.io/java/2017/10/29/spring-bean%E8%A3%85%E9%85%8D/" rel="alternate" type="text/html" title="Spring装配bean" /><published>2017-10-29T00:00:00+00:00</published><updated>2017-10-29T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/10/29/spring%20bean%E8%A3%85%E9%85%8D</id><content type="html" xml:base="https://yumao123.github.io/java/2017/10/29/spring-bean%E8%A3%85%E9%85%8D/">&lt;blockquote&gt;
  &lt;p&gt;Spring的Bean装配
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/1029_top.png&quot; alt=&quot;Fate&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;自动装配&quot;&gt;自动装配&lt;/h2&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;基于java的装配
@ComponentScan(basePackages = {&quot;springdemo.CD&quot;})
基于xml的配置
&amp;lt;context:component-scan/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;代码中的使用&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Component
public class CDPlayer implements MediaPlayer{
    private CompactDisc cd;
    @Autowired
    public CDPlayer(CompactDisc cd){
        this.cd = cd;
    }
    public void Play() {
        cd.play();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 自动创建spring应用上下文
@RunWith(SpringJUnit4ClassRunner.class)
// 通过CDPlayerConfig加载配置
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
    @Autowired
    private CompactDisc cd;
    @Autowired
    private MediaPlayer player;
    @Test
    public void cdShouldNotBeNull(){
        Assert.assertNotNull(cd);
    }
    @Test
    public void play(){
        player.Play();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Keywords:@ComponentScan,&lt;context:component-scan&gt;&lt;/context:component-scan&gt;,@Component,@Autowired&lt;/p&gt;

&lt;h2 id=&quot;基于java的装配&quot;&gt;基于Java的装配&lt;/h2&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// Configuration注解表明这个类时一个配置类，包括spring上下文如何创建bean的细节
@Configuration
// 自动装配这个包下的类
// @ComponentScan(basePackages = {&quot;springdemo.CD&quot;})
// 但是如果涉及一些第三方包，我们无法通过隐式的方式加载，就必须要显式配置
// 通过将其他java配置导入
@Import(AnotherConfig.class)
// java配置中加载xml配置
@ImportResource(&quot;classpath:CDPlayerXmlTest-context.xml&quot;)
public class CDPlayerConfig {

    // 生命简单的bean，返回一个对象
    @Bean
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }
    // 如果这样写，那么每个CDPlayer构造时都要有一份独立的CompactDisc，这显然时没有必要的
    @Bean
    public CDPlayer anotherCdPlayers(){
        return new CDPlayer(sgtPeppers());
    }
    // 通过请求一个CompactDisc作为参数，自动装配一个CompactDisc到配置方法
    @Bean
    public CDPlayer cdPlayer(CompactDisc cd){
        // 这里也可以通过setter的方式
        // CDPlayer cd = new CDPlayer();
        // cd.setCompactDisc(cd);
        // return cd;
        return new CDPlayer(cd);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;keywords:@Configuration,@Import,@ImportResource,@Bean&lt;/p&gt;

&lt;h2 id=&quot;通过xml装配&quot;&gt;通过xml装配&lt;/h2&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;beans&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.springframework.org/schema/beans&quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xmlns:c=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.springframework.org/schema/c&quot;&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--文件名称必须以-context.xml结尾--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--c- 开头 与&amp;lt;constructor-arg&amp;gt;功能一致--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--p- 开头与&amp;lt;property&amp;gt;功能一致--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;compactDisc&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;springdemo.CD.SgtPeppers&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;id=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;mediaPlayer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;springdemo.CD.CDPlayer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;c:cd-ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;compactDisc&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--导入其他的xmlconfg配置文件--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;import&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;resource=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AnotherXmlConfig-context.xml&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--导入其他的javaconfig配置文件--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;bean&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;springdemo.CD.AnotherConfig&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--不管使用javaconfig或xmlconfig，一般都会创建一个根配置，这个配置将更多的装配类或xml文件组装起来--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;&amp;lt;!--并且在跟配置启用组件扫描--&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/beans&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;环境profile&quot;&gt;环境profile&lt;/h2&gt;
&lt;p&gt;主要是为了生产环境/测试环境/开发环境中不同环境的bean装配(条件装配的一种)&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 可以在类加注解
// @Profile(&quot;dev&quot;)
public class DevelopmentProfileConfig {
    @Bean(destroyMethod = &quot;shutdown&quot;)
    @Profile(&quot;dev&quot;)
    public DataSource dataSource(){
        return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
                .addScript(&quot;classpath:schema.sql&quot;).build();
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;激活Profile&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;正式的web項目中，可以在web.xml中添加
  &amp;lt;context-param&amp;gt;
    &amp;lt;param-name&amp;gt;spring.profiles.default&amp;lt;/param-name&amp;gt;
    &amp;lt;param-value&amp;gt;dev&amp;lt;/param-value&amp;gt;
  &amp;lt;/context-param&amp;gt;
测试项目中，可以
@ActiveProfile(&quot;dev&quot;)
public class CDPlayerConfig {}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;keywords:@Profile,@ActiveProfile&lt;/p&gt;

&lt;h2 id=&quot;条件化装配bean&quot;&gt;条件化装配bean&lt;/h2&gt;
&lt;p&gt;首先要定义类实现接口Condition&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public class MagicExistsCondition implements Condition {
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment env = conditionContext.getEnvironment();
        System.out.println(env.getActiveProfiles());
        return env.containsProperty(&quot;magic&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    @Bean
    @Conditional(MagicExistsCondition.class)
    public CompactDisc sgtPeppers(){
        return new SgtPeppers();
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;这样，只有在上下文环境中有属性magic才会装配bean&lt;br /&gt;
keywords:@Conditional,Conditional&lt;/p&gt;

&lt;h2 id=&quot;自动装配歧义性的处理方式&quot;&gt;自动装配歧义性的处理方式&lt;/h2&gt;
&lt;p&gt;当自动装配@autowired的时候，如果有多个类都实现了这个接口，那么会抛出异常&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;首选bean&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  @Bean
  @Primary
  public CompactDisc sgtPeppers(){
      return new SgtPeppers();
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;问题:只能有一个被@Primary注解，若有多个实现同一接口的类注解则抛出异常&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;限定自装配bean&lt;br /&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;指定想要注入进去的是哪个bean
  @Autowired
  @Qualifier(&quot;cdPlayer&quot;)
  private MediaPlayer player;
创建自定义限定符
@Component
@Qualifier(&quot;cdPlayer&quot;)
public class CDPlayer implements MediaPlayer{}
以上是通过javaconfig配置的bean，则@Bean要与@Qualifier配合使用
  @Bean
  @Qualifier(&quot;cdPlayer&quot;)
  public CDPlayer cdPlayer(CompactDisc cd){
      return new CDPlayer(cd);
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;问题:如果一个类需要有多个限定符修饰，那么使用多个@Qualifier修饰是非法的，但是可以通过自定义限定符实现&lt;/p&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;自定义限定符，需要被@Qualifier注解
@Qualifier
public @interface Creamy{}
@Qualifier
public @interface Cold{}
使用:
@Creamy
@Cold
public class IceCream implements Dessert{}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;keywords:@Qualifier&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;bean作用域&quot;&gt;bean作用域&lt;/h2&gt;
&lt;p&gt;定义了多个作用域:Singleton(单例) Prototype(原型) Session(会话) Request(请求)&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CDPlayer implements MediaPlayer{}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;注意:如果一个bean注入的另一个bean生命周期比他小的话(例如单例与会话),则需要创建一个作用域代理?&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode=ScopedProxyMode.INTERFACES)
public class CDPlayer implements MediaPlayer{}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;xml中声明&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;bean id=&quot;cart&quot; class=&quot;xxx&quot; scope=&quot;session&quot;&amp;gt;
	&amp;lt;aop:scoped-proxy/&amp;gt;
&amp;lt;/bean&amp;gt;
注意需要在xml中声明命名空间
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;keywords:@Scope,scope&lt;/p&gt;

&lt;h2 id=&quot;运行时注入&quot;&gt;运行时注入&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;属性占位符
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;javaconfig配置bean的方式
@PropertySource(&quot;classpath:springdemo/app.properties&quot;)
public class CDPlayerConfig {
  @Bean
  @Qualifier(&quot;cdPlayer&quot;)
  public CDPlayer cdPlayer(CompactDisc cd){
      return new CDPlayer(cd, env.getProperty(&quot;disc.name&quot;));
  }	
}
这里将app.properties里的disc.name加入到构造函数中
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;解析属性占位符,占位符语法(${})&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;如果不通过javaconfig配置bean
&amp;lt;bean id=&quot;mediaPlayer&quot; class=&quot;springdemo.CD.CDPlayer&quot; c:cd-ref=&quot;compactDisc&quot; c:name=&quot;${disc.name}&quot;&amp;gt;
或者在定义类的时候，通过@Value
@Component
public class CDPlayer implements MediaPlayer{
    private CompactDisc cd;
    private String name;
    @Autowired
    public CDPlayer(CompactDisc cd, @Value(&quot;${disc.name}&quot;) String name){
        this.cd = cd;
        this.name = name;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;为了使用占位符，必须配置PropertySourcesPlaceholderConfigurer&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;javaconfig配置:
    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }
xml配置:
&amp;lt;context:property-placeholder/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;keywords:@PropertySource,env.getProperty,${},PropertySourcesPlaceholderConfigurer,&lt;context:property-placeholder&gt;&lt;/context:property-placeholder&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Spring表达式语言(SpEL)&lt;/li&gt;
&lt;/ul&gt;</content><author><name>Yumao</name></author><category term="Spring" /><summary type="html">Spring的Bean装配</summary></entry><entry><title type="html">Spring概述</title><link href="https://yumao123.github.io/java/2017/10/28/spring%E6%A6%82%E8%BF%B0/" rel="alternate" type="text/html" title="Spring概述" /><published>2017-10-28T00:00:00+00:00</published><updated>2017-10-28T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/10/28/spring%E6%A6%82%E8%BF%B0</id><content type="html" xml:base="https://yumao123.github.io/java/2017/10/28/spring%E6%A6%82%E8%BF%B0/">&lt;blockquote&gt;
  &lt;p&gt;关于Spring的简述
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/1028_top.png&quot; alt=&quot;Fate&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;简化java开发&quot;&gt;简化java开发&lt;/h2&gt;
&lt;p&gt;关键策略点
1.基于POJO的轻量级和最小侵入性编程
2.通过依赖注入和面向接口实现松耦合&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;紧耦合
private RescueDamseQuest quest;
public DamselRecuingKight(){
	this.quest = new RescueDamseQuest();
}
松耦合
private Quest quest;
public DamselRecuingKight(Quest quest){
	this.quest = quest;
}
这种就是构造注入
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在spring中，通过xml配置或者java配置实现依赖注入
3.基于切面和惯例进行声明式编程&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;public BraveKnight(Quest quest, Minstrel minstrel){
	...
}
public void embarkOnQuest(){
	minstrel.singBeforeQuest();
	quest.embark();
	minstrel.singAfterQuest();
}
可见,管理Minstrel其实并不是Knight的职责，并且如果以后需要一个不需要Minstrel的Knight,代码复杂度过高
将Minstrel声明为一个切片可以免去这些繁琐的代码
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;4.通过切面和模板减少样板式代码&lt;/p&gt;

&lt;h2 id=&quot;容纳bean&quot;&gt;容纳bean&lt;/h2&gt;
&lt;p&gt;容器时spring框架的核心，使用DI管理构成应用的组件，创建相互协作的组件关联
spring容器归为：
bean工厂：最简单的容器，提供DI支持
应用上下文：基于BeanFactory构建，提供框架级别的服务&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;应用上下文
AnnotationConfigApplicationContext：从一个或多个基于Java的配置类中加载Spring应用上下文。
AnnotationConfigWebApplicationContext：从一个或多个基于Java的配置类中加载Spring Web应用上下文。
ClassPathXmlApplicationContext：从类路径下的一个或多个XML配置文件中加载上下文定义，把应用上下文的定义文件作为类资源。
FileSystemXmlapplicationcontext：从文件系统下的一个或多个XML配置文件中加载上下文定义。
XmlWebApplicationContext：从Web应用下的一个或多个XML配置文件中加载上下文定义&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;bean生命周期&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;spring模块&quot;&gt;Spring模块&lt;/h2&gt;
&lt;p&gt;Spring核心容器
容器是Spring框架最核心的部分，它管理着Spring应用中bean的创建、配置和管理。在该模块中，包括了Spring bean工厂，它为Spring提供了DI的功能。基于bean工厂，我们还会发现有多种Spring应用上下文的实现，每一种都提供了配置Spring的不同方式
Spring的AOP模块
在AOP模块中，Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础。
数据访问与集成
Web与远程调用&lt;/p&gt;</content><author><name>Yumao</name></author><category term="Spring" /><summary type="html">关于Spring的简述</summary></entry><entry><title type="html">Jvm线程安全与锁优化</title><link href="https://yumao123.github.io/java/2017/10/26/jvm%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E4%B8%8E%E9%94%81%E4%BC%98%E5%8C%96/" rel="alternate" type="text/html" title="Jvm线程安全与锁优化" /><published>2017-10-26T00:00:00+00:00</published><updated>2017-10-26T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/10/26/jvm%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E4%B8%8E%E9%94%81%E4%BC%98%E5%8C%96</id><content type="html" xml:base="https://yumao123.github.io/java/2017/10/26/jvm%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E4%B8%8E%E9%94%81%E4%BC%98%E5%8C%96/">&lt;blockquote&gt;
  &lt;p&gt;Jvm线程安全与锁优化
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/1026_top.png&quot; alt=&quot;Fate&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;关于线程安全&quot;&gt;关于线程安全&lt;/h2&gt;
&lt;p&gt;线程安全：当多个线程访问一个对象，不用考虑这些线程在运行时环境下的调度和交替工作，都可以获得正确的结果，那么就是线程安全&lt;br /&gt;
即代码本身封装了所有必要的保障手段，调用者无须关心多线程带来的问题&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;共享数据分类&lt;br /&gt;
1.不可变&lt;br /&gt;
如果共享的数据是一个基本类型，只要final修饰即可；如果是一个对象，则需要保证对象行为不会对其状态产生任何影响，例如将对象中带有状态的变量声明为final
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Integer{
  private final int value;
  public Integer(int value){this.value = value;}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;2.绝对线程安全&lt;br /&gt;
java api自己的一些线程安全类大多数都不是绝对的线程安全&lt;/p&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Vector&amp;lt;Integer&amp;gt; ve = new Vector&amp;lt;&amp;gt;();
此时向ve注入一些数据，两个线程一个删除，一个读取，那么在一定情况下，可能会由于一个线程删除了一个元素，导致另一个线程通过序号访问时，序号不可用，抛出越界异常
此时还是需要在调用端通过synchronized(ve){dosomething()..}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;3.相对线程安全&lt;br /&gt;
大多数线程安全类都属于相对线程安全，就是说只能保证这个对象单独操作是线程安全的，但是对于一些特定顺序连续调用，可能需要在调用端试用结果额外同步手段保证调用正确性&lt;br /&gt;
4.线程兼容&lt;br /&gt;
对象本身不是线程安全，但是通过调用端使用同步手段保证对象在并发环境中可安全调用&lt;br /&gt;
5.线程对立&lt;br /&gt;
无论调用端是否采取了同步措施，都无法再多线程环境中并发使用的代码&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;线程安全的实现方法&quot;&gt;线程安全的实现方法&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;互斥同步&lt;br /&gt;
指的就是多线程访问共享数据时，保证共享数据在同一时刻只被一个线程使用&lt;br /&gt;
synchronized：&lt;br /&gt;
会在同步块的前后行程monitorenter和monitorexit字节码指令，并且需要一个reference类型参数指名锁定和解锁的对象；如果指名了对象参数，就是这个对象的refernce，否则根据synchronized修饰的是实力方法or类方法，去取对应对象实例or Class对象作为锁对象&lt;br /&gt;
当执行到monitorenter时，首先尝试对象是否锁定，如果没有锁定或当前线程已有那个对象的锁，就将锁的计数器+1；在执行monitorexit时，会将锁的计数器-1，至计数器=0，锁被释放成功；如果获取对象锁失败，则线程进入阻塞等待&lt;br /&gt;
由于java线程是映射到操作系统原生线程的，所以在阻塞或唤醒线程都是需要从用户态转换到核心态，存在耗时&lt;br /&gt;
ReentrantLock：&lt;br /&gt;
java.util.concurrent(JUC)中的重入锁&lt;br /&gt;
向对synchronized提供了一些更高级的功能:&lt;br /&gt;
1.等待可中断：等待的线程可以选择放弃等待，处理其他事情&lt;br /&gt;
2.公平锁：多个线程等待同一个锁时，必须按照申请锁的时间顺序获取锁
3.锁绑定多个条件：一个ReentrantLock对象可绑定多个condition对象，synchronized如果要完成相同功能则需要额外添加一个锁&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;非阻塞同步&lt;br /&gt;
先操作，如果没有其他线程用共享数据，操作成功；若有争用，发生冲突时再采取其他补偿措施(不断重试，直到成功)，不需要将线程挂起&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;无同步方案&lt;br /&gt;
如果一个方法不涉及共享数据，那么无须同步措施去保证正确性，有些代码天生就是线程安全&lt;br /&gt;
可重入代码&lt;br /&gt;
不依赖存储在堆上的数据和公用系统资源，用到的状态量由参数传入，不调用非可重入的方法
线程本地存储&lt;br /&gt;
如果能保证共享数据的代码在同一线程执行，就无需同步也能保证线程之间不出现数据争用&lt;br /&gt;
例如 生产者 - 消费者模式、ThreadLocal&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;锁优化&quot;&gt;锁优化&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;自旋锁与自适应自旋&lt;br /&gt;
因为互斥同步对性能最大影响是阻塞的实现，所以如果物理机有一个以上处理器，就能让两个或以上线程并行执行，则可以让后面请I去锁的线程稍等(不放弃处理器执行时间)，进入自旋(忙循环)
自旋锁占用处理器时间，若锁占用时间短，则自旋等待效果很好，当超过限定次数还没获取锁，则会使用传统方式挂起线程&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;锁消除&lt;br /&gt;
如果代码中存在一些上了锁但是锁上的对象是不可能存在共享数据竞争的，则会将这些锁消除掉。一句是逃逸分析的数据支持，若不会堆逃逸，则将他们当作栈上数据对待，则线程私有&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;锁粗化&lt;br /&gt;
当虚拟机探测到连续的操作对同一个对象反复加锁解锁，则会将范围扩展到整个操作序列外部&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;轻量级锁&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;偏向锁&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>Yumao</name></author><category term="Jvm" /><summary type="html">Jvm线程安全与锁优化</summary></entry><entry><title type="html">Jvm内存模型与线程</title><link href="https://yumao123.github.io/java/2017/10/25/jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B/" rel="alternate" type="text/html" title="Jvm内存模型与线程" /><published>2017-10-25T00:00:00+00:00</published><updated>2017-10-25T00:00:00+00:00</updated><id>https://yumao123.github.io/java/2017/10/25/jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B</id><content type="html" xml:base="https://yumao123.github.io/java/2017/10/25/jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B/">&lt;blockquote&gt;
  &lt;p&gt;关于Jvm内存模型与线程
主要介绍虚拟机如何实现多线程、多线程之间共享竞争数据导致的问题及解决方案
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/1025_top.png&quot; alt=&quot;Fate&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;关于内存模型&quot;&gt;关于内存模型&lt;/h2&gt;
&lt;p&gt;基于高速缓存解决了处理器与内存之间的速度矛盾，但是每个处理器都有自己的告诉缓存，然而共享同一主内存，为了避免各个缓存数据的不一致，牵扯到一个缓存的一致性问题，所以各个处理器访问缓存时都要遵循一些操作协议&lt;br /&gt;
内存模型：特定操作协议下，对特定内存或高速缓存进行读写访问&lt;br /&gt;&lt;/p&gt;

&lt;h2 id=&quot;java内存模型&quot;&gt;Java内存模型&lt;/h2&gt;
&lt;p&gt;Java内存模型：用于屏蔽各种硬件和操作系统的内存访问差异&lt;br /&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;主内存&amp;amp;工作内存&lt;br /&gt;
Java内存模型主要是定义程序中各个变量的访问规则(包括实例变量、静态变量和构成数组对象的元素，但不包括局部变量和方法参数，因为这些是线程私有)&lt;br /&gt;
Java内存模型规定所有变量存储在主内存中(类似前面共享的主内存)；每条线程还有自己的工作内存(类似处理器的高速缓存)；线程的工作内存中保留了被该线程使用变量的主内存副本拷贝，线程对变量的所有操作都必须在工作内存中进行；不同线程无法直接访问对方工作内存中的变量；线程间变量值传递需要通过主内存完成&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;内存间交互操作&lt;br /&gt;
那么变量是如何从主内存拷贝到工作内存，又是如何从工作内存同步主内存的呢？主要通过8种操作
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/thread2.png&quot; alt=&quot;如图&quot; /&gt;
java内存模型还规定了几个必须满足的规则:&lt;br /&gt;
1.一个变量同一时刻只允许一条线程对其进行lock，但一个线程可以对其Lock多次，然而也有执行相应次数的unlock，变量才能被正确解锁&lt;br /&gt;
2.如果堆变量执行Lock，会清空工作内存中此变量的值，执行引擎使用这个变量前，要重新执行Load,assign操作初始化&lt;br /&gt;
3.如果一个变量没有被lock，那么也不可以被unlock，也不允许unlock一个被其他线程lock的变量
4.在unlock前，需要将变量同步回主内存中&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;关于volatile变量的特殊规则&lt;br /&gt;
volatile具备两种特性：&lt;br /&gt;
1.保证此变量对所有线程可见(一个线程改了值，另一个线程是立即可知的)&lt;br /&gt;
Volatile修饰的成员变量在每次被线程访问时，都强迫从共享内存中重读该成员变量的值。而且，当成员变量发生变化时，强迫线程将变化值回写到共享内存&lt;br /&gt;
由于java的运算非原子操作，volatile变量的运算在并发下是不安全的&lt;br /&gt;
eg:
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;for(;;){
  something 
  // 如果线程内该值没有被volatile修饰，那么在进入线程时，只会从主内存同步一次这个值
  // 如果线程内该值被volatile修饰，那么每次访问这个变量都会从主内存中重新读取该值
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;2.禁止指令重排序优化&lt;/p&gt;
    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;线程a
static boolean flag = false;
dosomething()...
flag = true;
线程b
while(!flag){
  // sleep()
}
dosomething()...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;上面如果flag不用volatile修饰，则flag = true可能被提前执行(指令重排序优化)&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;volatile的作用&lt;br /&gt;
volatile同步性能优于锁&lt;br /&gt;
注：线程T，V/W表示两个volatile变量&lt;br /&gt;
1.线程对V的操作必须要满足顺序如下：load -&amp;gt; use ; use -&amp;gt; load；也就是要求在工作内存中，每次使用V前都要先从主内存刷新最新值&lt;br /&gt;
2.线程对V的操作必须满足：assign -&amp;gt; store ; store -&amp;gt; aassign；也就是要求在工作内存中，每次修改V后都必须同步到主内存中&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;long&amp;amp;double特殊规则&lt;br /&gt;
虽然java内存模型要求上述8个操作都具有原子性，但是对于64位数据类型，则规定：&lt;br /&gt;
允许虚拟机将选择不保证没有被volatile修饰的64位数据类型的load,store,read,write的原子性，也就是如果多个线程同时共享一个未声明volatile的long或double，则有可能取到一个既非原值，又不是其他线程修改后的数值&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;原子性 可见性 有序性&lt;br /&gt;
原子性：由java内存模型保证的原子性包括：load,store,read,write,assign,use;提供了monitorenter和monitorexit隐式的使用者两个操作，反应到java代码便是同步块synchronized&lt;br /&gt;
可见性：当一个线程修改了共享变量的值，其他线程能立刻得知，普通变量和volatile区别是，volatile保证了新值能立即同步到主内存，以及每次使用前都立即从主内存刷新；&lt;br /&gt;
除了volatile，java还有synchronized和final能保证可见性&lt;br /&gt;
synchronized是由对一个变量执行unlock前，会将变量同步回主内存&lt;br /&gt;
final是指被final修饰的字段在构造器中一旦初始化完成，其他线程就能看到final字段的值
有序性：如果在本线程内观察，所有操作都是有序的，如果在一个线程中观察另一个线程，所有操作都是无序的&lt;br /&gt;
java提供了volatile和synchronized关键字保证操作的有序性，synchronzied确保一个变量在同一时刻只能允许一条线程对其进行lock操作，决定持有同一个锁的两个同步块只能串行进入&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;先行发生原则&lt;br /&gt;
是判断数据是否存在竞争、线程是否安全的主要依据&lt;br /&gt;
什么是先行发生？&lt;br /&gt;
指java内存模型中定义的两个操作之间的偏序关系，如果说操作A先行发生于操作B，就是说操作B前，操作A产生的影响能被操作B观察到&lt;br /&gt;
如果两个操作关系不在以下列，则没有顺序性保障，虚拟机可以对他们随意重排序
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/thread3.png&quot; alt=&quot;如图&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;java的线程&quot;&gt;Java的线程&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;线程的实现&lt;br /&gt;
1.使用内核线程&lt;br /&gt;
直接由操作系统内核支持的线程，一般轻量级进程就是我们通常所讲的线程&lt;br /&gt;
2.使用用户线程&lt;br /&gt;
用户线程的简历、同步、销毁和调度在用户态中完成，不需要内核的帮助，java等语言已经放弃使用用户线程了&lt;br /&gt;
3.使用用户线程加轻量级进程&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Java线程的实现还是使用一对一的模型实现，即一条java线程映射到一条轻量级进程中&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;java线程调度(CPU的使用权)&lt;br /&gt;
1.协同式&lt;br /&gt;
执行时间由线程本身控制，自己处理完后通知系统切换到另一个线程&lt;br /&gt;
优势：简单，且没有现成同步问题&lt;br /&gt;
劣势：执行时间不可控，可能会导致程序阻塞&lt;br /&gt;
2.抢占式&lt;br /&gt;
执行时间由系统分配(在java中可以通过yield让出执行时间,但是无法获取执行时间)&lt;br /&gt;
优势：执行时间系统可控，不会有一个线程导致进行阻塞&lt;br /&gt;
java可以通过设置优先级，给线程分配时间&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;状态转换&lt;br /&gt;
java定义了5种线程状态，任意一个时间点，一个线程只能有且只有一种状态
&lt;img src=&quot;https://yumao123.github.io/assets/images/201710/thread4.png&quot; alt=&quot;如图&quot; /&gt;
注：wait和block的区别是，阻塞是等待着获取一个排它锁；而等待是等待一段时间或唤醒动作的发生.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name>Yumao</name></author><category term="Jvm" /><summary type="html">关于Jvm内存模型与线程 主要介绍虚拟机如何实现多线程、多线程之间共享竞争数据导致的问题及解决方案</summary></entry></feed>